| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,263 @@ | ||
|
|
||
| # REQUIRES: system-linux | ||
|
|
||
| # RUN: llvm-mc -dwarf-version=4 -filetype=obj -triple x86_64-unknown-linux %s -o %tmain.o | ||
| # RUN: %clang %cflags -dwarf-4 %tmain.o -o %t.exe -Wl,-q | ||
| # RUN: llvm-bolt %t.exe -o %t.bolt --update-debug-sections | ||
| # RUN: llvm-dwarfdump --show-form --verbose --debug-info %t.exe | FileCheck --check-prefix=PRECHECK %s | ||
| # RUN: llvm-dwarfdump --show-form --verbose --debug-info %t.bolt > %t.txt | ||
| # RUN: llvm-objdump -d %t.bolt >> %t.txt | ||
| # RUN: cat %t.txt | FileCheck --check-prefix=POSTCHECK %s | ||
|
|
||
| ## This test checks that we correctly handle DW_AT_low_pc [DW_FORM_addr] that is part of DW_TAG_label. | ||
|
|
||
| # PRECHECK: version = 0x0004 | ||
| # PRECHECK: DW_TAG_label | ||
| # PRECHECK-NEXT: DW_AT_name | ||
| # PRECHECK-NEXT: DW_AT_decl_file | ||
| # PRECHECK-NEXT: DW_AT_decl_line | ||
| # PRECHECK-NEXT:DW_AT_low_pc [DW_FORM_addr] | ||
| # PRECHECK: DW_TAG_label | ||
| # PRECHECK-NEXT: DW_AT_name | ||
| # PRECHECK-NEXT: DW_AT_decl_file | ||
| # PRECHECK-NEXT: DW_AT_decl_line | ||
| # PRECHECK-NEXT:DW_AT_low_pc [DW_FORM_addr] | ||
|
|
||
| # POSTCHECK: version = 0x0004 | ||
| # POSTCHECK: DW_TAG_label | ||
| # POSTCHECK-NEXT: DW_AT_name | ||
| # POSTCHECK-NEXT: DW_AT_decl_file | ||
| # POSTCHECK-NEXT: DW_AT_decl_line | ||
| # POSTCHECK-NEXT:DW_AT_low_pc [DW_FORM_addr] (0x[[ADDR:[1-9a-f]*]] | ||
| # POSTCHECK: DW_TAG_label | ||
| # POSTCHECK-NEXT: DW_AT_name | ||
| # POSTCHECK-NEXT: DW_AT_decl_file | ||
| # POSTCHECK-NEXT: DW_AT_decl_line | ||
| # POSTCHECK-NEXT:DW_AT_low_pc [DW_FORM_addr] (0x[[ADDR2:[1-9a-f]*]] | ||
|
|
||
| # POSTCHECK: [[ADDR]]: 8b 45 f8 | ||
| # POSTCHECK: [[ADDR2]]: 8b 45 f8 | ||
|
|
||
| ## clang++ main.cpp -g2 -gdwarf-4 -S | ||
| ## int main() { | ||
| ## int a = 4; | ||
| ## if (a == 5) | ||
| ## goto LABEL1; | ||
| ## else | ||
| ## goto LABEL2; | ||
| ## LABEL1:a++; | ||
| ## LABEL2:a--; | ||
| ## return 0; | ||
| ## } | ||
|
|
||
| .text | ||
| .file "main.cpp" | ||
| .globl main # -- Begin function main | ||
| .p2align 4, 0x90 | ||
| .type main,@function | ||
| main: # @main | ||
| .Lfunc_begin0: | ||
| .file 1 "/home" "main.cpp" | ||
| .loc 1 1 0 # main.cpp:1:0 | ||
| .cfi_startproc | ||
| # %bb.0: # %entry | ||
| pushq %rbp | ||
| .cfi_def_cfa_offset 16 | ||
| .cfi_offset %rbp, -16 | ||
| movq %rsp, %rbp | ||
| .cfi_def_cfa_register %rbp | ||
| movl $0, -4(%rbp) | ||
| .Ltmp0: | ||
| .loc 1 2 7 prologue_end # main.cpp:2:7 | ||
| movl $4, -8(%rbp) | ||
| .Ltmp1: | ||
| .loc 1 3 9 # main.cpp:3:9 | ||
| cmpl $5, -8(%rbp) | ||
| .Ltmp2: | ||
| .loc 1 3 7 is_stmt 0 # main.cpp:3:7 | ||
| jne .LBB0_2 | ||
| # %bb.1: # %if.then | ||
| .Ltmp3: | ||
| .loc 1 4 5 is_stmt 1 # main.cpp:4:5 | ||
| jmp .LBB0_3 | ||
| .LBB0_2: # %if.else | ||
| .loc 1 6 5 # main.cpp:6:5 | ||
| jmp .LBB0_4 | ||
| .Ltmp4: | ||
| .LBB0_3: # %LABEL1 | ||
| #DEBUG_LABEL: main:LABEL1 | ||
| .loc 1 7 11 # main.cpp:7:11 | ||
| movl -8(%rbp), %eax | ||
| addl $1, %eax | ||
| movl %eax, -8(%rbp) | ||
| .LBB0_4: # %LABEL2 | ||
| .Ltmp5: | ||
| #DEBUG_LABEL: main:LABEL2 | ||
| .loc 1 8 11 # main.cpp:8:11 | ||
| movl -8(%rbp), %eax | ||
| addl $-1, %eax | ||
| movl %eax, -8(%rbp) | ||
| .loc 1 9 3 # main.cpp:9:3 | ||
| xorl %eax, %eax | ||
| .loc 1 9 3 epilogue_begin is_stmt 0 # main.cpp:9:3 | ||
| popq %rbp | ||
| .cfi_def_cfa %rsp, 8 | ||
| retq | ||
| .Ltmp6: | ||
| .Lfunc_end0: | ||
| .size main, .Lfunc_end0-main | ||
| .cfi_endproc | ||
| # -- End function | ||
| .section .debug_abbrev,"",@progbits | ||
| .byte 1 # Abbreviation Code | ||
| .byte 17 # DW_TAG_compile_unit | ||
| .byte 1 # DW_CHILDREN_yes | ||
| .byte 37 # DW_AT_producer | ||
| .byte 14 # DW_FORM_strp | ||
| .byte 19 # DW_AT_language | ||
| .byte 5 # DW_FORM_data2 | ||
| .byte 3 # DW_AT_name | ||
| .byte 14 # DW_FORM_strp | ||
| .byte 16 # DW_AT_stmt_list | ||
| .byte 23 # DW_FORM_sec_offset | ||
| .byte 27 # DW_AT_comp_dir | ||
| .byte 14 # DW_FORM_strp | ||
| .byte 17 # DW_AT_low_pc | ||
| .byte 1 # DW_FORM_addr | ||
| .byte 18 # DW_AT_high_pc | ||
| .byte 6 # DW_FORM_data4 | ||
| .byte 0 # EOM(1) | ||
| .byte 0 # EOM(2) | ||
| .byte 2 # Abbreviation Code | ||
| .byte 46 # DW_TAG_subprogram | ||
| .byte 1 # DW_CHILDREN_yes | ||
| .byte 17 # DW_AT_low_pc | ||
| .byte 1 # DW_FORM_addr | ||
| .byte 18 # DW_AT_high_pc | ||
| .byte 6 # DW_FORM_data4 | ||
| .byte 64 # DW_AT_frame_base | ||
| .byte 24 # DW_FORM_exprloc | ||
| .byte 3 # DW_AT_name | ||
| .byte 14 # DW_FORM_strp | ||
| .byte 58 # DW_AT_decl_file | ||
| .byte 11 # DW_FORM_data1 | ||
| .byte 59 # DW_AT_decl_line | ||
| .byte 11 # DW_FORM_data1 | ||
| .byte 73 # DW_AT_type | ||
| .byte 19 # DW_FORM_ref4 | ||
| .byte 63 # DW_AT_external | ||
| .byte 25 # DW_FORM_flag_present | ||
| .byte 0 # EOM(1) | ||
| .byte 0 # EOM(2) | ||
| .byte 3 # Abbreviation Code | ||
| .byte 52 # DW_TAG_variable | ||
| .byte 0 # DW_CHILDREN_no | ||
| .byte 2 # DW_AT_location | ||
| .byte 24 # DW_FORM_exprloc | ||
| .byte 3 # DW_AT_name | ||
| .byte 14 # DW_FORM_strp | ||
| .byte 58 # DW_AT_decl_file | ||
| .byte 11 # DW_FORM_data1 | ||
| .byte 59 # DW_AT_decl_line | ||
| .byte 11 # DW_FORM_data1 | ||
| .byte 73 # DW_AT_type | ||
| .byte 19 # DW_FORM_ref4 | ||
| .byte 0 # EOM(1) | ||
| .byte 0 # EOM(2) | ||
| .byte 4 # Abbreviation Code | ||
| .byte 10 # DW_TAG_label | ||
| .byte 0 # DW_CHILDREN_no | ||
| .byte 3 # DW_AT_name | ||
| .byte 14 # DW_FORM_strp | ||
| .byte 58 # DW_AT_decl_file | ||
| .byte 11 # DW_FORM_data1 | ||
| .byte 59 # DW_AT_decl_line | ||
| .byte 11 # DW_FORM_data1 | ||
| .byte 17 # DW_AT_low_pc | ||
| .byte 1 # DW_FORM_addr | ||
| .byte 0 # EOM(1) | ||
| .byte 0 # EOM(2) | ||
| .byte 5 # Abbreviation Code | ||
| .byte 36 # DW_TAG_base_type | ||
| .byte 0 # DW_CHILDREN_no | ||
| .byte 3 # DW_AT_name | ||
| .byte 14 # DW_FORM_strp | ||
| .byte 62 # DW_AT_encoding | ||
| .byte 11 # DW_FORM_data1 | ||
| .byte 11 # DW_AT_byte_size | ||
| .byte 11 # DW_FORM_data1 | ||
| .byte 0 # EOM(1) | ||
| .byte 0 # EOM(2) | ||
| .byte 0 # EOM(3) | ||
| .section .debug_info,"",@progbits | ||
| .Lcu_begin0: | ||
| .long .Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit | ||
| .Ldebug_info_start0: | ||
| .short 4 # DWARF version number | ||
| .long .debug_abbrev # Offset Into Abbrev. Section | ||
| .byte 8 # Address Size (in bytes) | ||
| .byte 1 # Abbrev [1] 0xb:0x6d DW_TAG_compile_unit | ||
| .long .Linfo_string0 # DW_AT_producer | ||
| .short 33 # DW_AT_language | ||
| .long .Linfo_string1 # DW_AT_name | ||
| .long .Lline_table_start0 # DW_AT_stmt_list | ||
| .long .Linfo_string2 # DW_AT_comp_dir | ||
| .quad .Lfunc_begin0 # DW_AT_low_pc | ||
| .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc | ||
| .byte 2 # Abbrev [2] 0x2a:0x46 DW_TAG_subprogram | ||
| .quad .Lfunc_begin0 # DW_AT_low_pc | ||
| .long .Lfunc_end0-.Lfunc_begin0 # DW_AT_high_pc | ||
| .byte 1 # DW_AT_frame_base | ||
| .byte 86 | ||
| .long .Linfo_string3 # DW_AT_name | ||
| .byte 1 # DW_AT_decl_file | ||
| .byte 1 # DW_AT_decl_line | ||
| .long 112 # DW_AT_type | ||
| # DW_AT_external | ||
| .byte 3 # Abbrev [3] 0x43:0xe DW_TAG_variable | ||
| .byte 2 # DW_AT_location | ||
| .byte 145 | ||
| .byte 120 | ||
| .long .Linfo_string5 # DW_AT_name | ||
| .byte 1 # DW_AT_decl_file | ||
| .byte 2 # DW_AT_decl_line | ||
| .long 112 # DW_AT_type | ||
| .byte 4 # Abbrev [4] 0x51:0xf DW_TAG_label | ||
| .long .Linfo_string6 # DW_AT_name | ||
| .byte 1 # DW_AT_decl_file | ||
| .byte 7 # DW_AT_decl_line | ||
| .quad .Ltmp4 # DW_AT_low_pc | ||
| .byte 4 # Abbrev [4] 0x60:0xf DW_TAG_label | ||
| .long .Linfo_string7 # DW_AT_name | ||
| .byte 1 # DW_AT_decl_file | ||
| .byte 8 # DW_AT_decl_line | ||
| .quad .Ltmp5 # DW_AT_low_pc | ||
| .byte 0 # End Of Children Mark | ||
| .byte 5 # Abbrev [5] 0x70:0x7 DW_TAG_base_type | ||
| .long .Linfo_string4 # DW_AT_name | ||
| .byte 5 # DW_AT_encoding | ||
| .byte 4 # DW_AT_byte_size | ||
| .byte 0 # End Of Children Mark | ||
| .Ldebug_info_end0: | ||
| .section .debug_str,"MS",@progbits,1 | ||
| .Linfo_string0: | ||
| .asciz "clang version 19.0.0git" # string offset=0 | ||
| .Linfo_string1: | ||
| .asciz "main.cpp" # string offset=24 | ||
| .Linfo_string2: | ||
| .asciz "/home" # string offset=33 | ||
| .Linfo_string3: | ||
| .asciz "main" # string offset=71 | ||
| .Linfo_string4: | ||
| .asciz "int" # string offset=76 | ||
| .Linfo_string5: | ||
| .asciz "a" # string offset=80 | ||
| .Linfo_string6: | ||
| .asciz "LABEL1" # string offset=82 | ||
| .Linfo_string7: | ||
| .asciz "LABEL2" # string offset=89 | ||
| .ident "clang version 19.0.0git" | ||
| .section ".note.GNU-stack","",@progbits | ||
| .addrsig | ||
| .section .debug_line,"",@progbits | ||
| .Lline_table_start0: |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| // RUN: %clang_cc1 -ast-dump %s | FileCheck %s | ||
|
|
||
| /* WG14 N1365: Clang 16 | ||
| * Constant expressions | ||
| */ | ||
|
|
||
| // Note: we don't allow you to expand __FLT_EVAL_METHOD__ in the presence of a | ||
| // pragma that changes its value. However, we can test that we have the correct | ||
| // constant expression behavior by testing that the AST has the correct implicit | ||
| // casts, which also specify that the cast was inserted due to an evaluation | ||
| // method requirement. | ||
| void func(void) { | ||
| { | ||
| #pragma clang fp eval_method(double) | ||
| _Static_assert(123.0F * 2.0F == 246.0F, ""); | ||
| // CHECK: StaticAssertDecl | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <IntegralToBoolean> | ||
| // CHECK-NEXT: BinaryOperator {{.*}} 'int' '==' | ||
| // CHECK-NEXT: BinaryOperator {{.*}} 'double' '*' FPEvalMethod=1 | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast> FPEvalMethod=1 | ||
| // CHECK-NEXT: FloatingLiteral | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast> FPEvalMethod=1 | ||
| // CHECK-NEXT: FloatingLiteral | ||
|
|
||
| // Ensure that a cast removes the extra precision. | ||
| _Static_assert(123.0F * 2.0F == 246.0F, ""); | ||
| // CHECK: StaticAssertDecl | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <IntegralToBoolean> | ||
| // CHECK-NEXT: BinaryOperator {{.*}} 'int' '==' | ||
| // CHECK-NEXT: BinaryOperator {{.*}} 'double' '*' FPEvalMethod=1 | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast> FPEvalMethod=1 | ||
| // CHECK-NEXT: FloatingLiteral | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast> FPEvalMethod=1 | ||
| // CHECK-NEXT: FloatingLiteral | ||
| } | ||
|
|
||
| { | ||
| #pragma clang fp eval_method(extended) | ||
| _Static_assert(123.0F * 2.0F == 246.0F, ""); | ||
| // CHECK: StaticAssertDecl | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <IntegralToBoolean> | ||
| // CHECK-NEXT: BinaryOperator {{.*}} 'int' '==' | ||
| // CHECK-NEXT: BinaryOperator {{.*}} 'long double' '*' FPEvalMethod=2 | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <FloatingCast> FPEvalMethod=2 | ||
| // CHECK-NEXT: FloatingLiteral | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'long double' <FloatingCast> FPEvalMethod=2 | ||
| // CHECK-NEXT: FloatingLiteral | ||
| } | ||
|
|
||
| { | ||
| #pragma clang fp eval_method(source) | ||
| _Static_assert(123.0F * 2.0F == 246.0F, ""); | ||
| // CHECK: StaticAssertDecl | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <IntegralToBoolean> | ||
| // CHECK-NEXT: BinaryOperator {{.*}} 'int' '==' | ||
| // CHECK-NEXT: BinaryOperator {{.*}} 'float' '*' FPEvalMethod=0 | ||
| // CHECK-NEXT: FloatingLiteral | ||
| // CHECK-NEXT: FloatingLiteral | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| /* RUN: %clang_cc1 -fsyntax-only -ast-dump %s | FileCheck %s | ||
| */ | ||
|
|
||
| /* WG14 DR290: no | ||
| * FLT_EVAL_METHOD and extra precision and/or range | ||
| * | ||
| * We retain an implicit conversion based on the float eval method being used | ||
| * instead of dropping it due to the explicit cast. See GH86304 and C23 6.5.5p7. | ||
| */ | ||
|
|
||
| #pragma clang fp eval_method(double) | ||
| _Static_assert((float)(123.0F * 2.0F) == (float)246.0F, ""); | ||
|
|
||
| // CHECK: StaticAssertDecl | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <IntegralToBoolean> | ||
| // CHECK-NEXT: BinaryOperator {{.*}} 'int' '==' | ||
| // NB: the following implicit cast is incorrect. | ||
| // CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <FloatingCast> FPEvalMethod=1 | ||
| // CHECK-NEXT: CStyleCastExpr {{.*}} 'float' <FloatingCast> FPEvalMethod=1 | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| // RUN: rm -rf %t | ||
| // RUN: split-file %s %t | ||
|
|
||
| // This test checks that source files with uncommon extensions still undergo | ||
| // dependency directives scan. If header.pch would not and b.h would, the scan | ||
| // would fail when parsing `void function(B)` and not knowing the symbol B. | ||
|
|
||
| //--- module.modulemap | ||
| module __PCH { header "header.pch" } | ||
| module B { header "b.h" } | ||
|
|
||
| //--- header.pch | ||
| #include "b.h" | ||
| void function(B); | ||
|
|
||
| //--- b.h | ||
| typedef int B; | ||
|
|
||
| //--- tu.c | ||
| int main() { | ||
| function(0); | ||
| return 0; | ||
| } | ||
|
|
||
| //--- cdb.json.in | ||
| [{ | ||
| "directory": "DIR", | ||
| "file": "DIR/tu.c", | ||
| "command": "clang -c DIR/tu.c -fmodules -fmodules-cache-path=DIR/cache -fimplicit-module-maps -include DIR/header.pch" | ||
| }] | ||
|
|
||
| // RUN: sed -e "s|DIR|%/t|g" %t/cdb.json.in > %t/cdb.json | ||
| // RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full > %t/deps.json |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| // RUN: %clang -emit-llvm -DCOUNTED_BY -S -g %s -o - | FileCheck %s | ||
| // RUN: %clang -emit-llvm -S -g %s -o - | FileCheck %s | ||
|
|
||
| #ifdef COUNTED_BY | ||
| #define __counted_by(member) __attribute__((__counted_by__(member))) | ||
| #else | ||
| #define __counted_by(member) | ||
| #endif | ||
|
|
||
| struct { | ||
| int num_counters; | ||
| long value[] __counted_by(num_counters); | ||
| } agent_send_response_port_num; | ||
|
|
||
| // CHECK: !DICompositeType(tag: DW_TAG_array_type, baseType: ![[BT:.*]], elements: ![[ELEMENTS:.*]]) | ||
| // CHECK: ![[BT]] = !DIBasicType(name: "long", size: {{.*}}, encoding: DW_ATE_signed) | ||
| // CHECK: ![[ELEMENTS]] = !{![[COUNT:.*]]} | ||
| // CHECK: ![[COUNT]] = !DISubrange(count: -1) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # Get the linker version on Darwin | ||
| function(get_darwin_linker_version variable) | ||
| set(LINK_VERSION) | ||
| set(LD_V_OUTPUT) | ||
| execute_process( | ||
| COMMAND sh -c "${CMAKE_LINKER} -v 2>&1 | head -1" | ||
| RESULT_VARIABLE HAD_ERROR | ||
| OUTPUT_VARIABLE LD_V_OUTPUT | ||
| ) | ||
| if (HAD_ERROR) | ||
| message(FATAL_ERROR "${CMAKE_LINKER} failed with status ${HAD_ERROR}") | ||
| endif() | ||
| if ("${LD_V_OUTPUT}" MATCHES ".*ld64-([0-9.]+).*") | ||
| string(REGEX REPLACE ".*ld64-([0-9.]+).*" "\\1" LINK_VERSION ${LD_V_OUTPUT}) | ||
| elseif ("${LD_V_OUTPUT}" MATCHES "[^0-9]*([0-9.]+).*") | ||
| string(REGEX REPLACE "[^0-9]*([0-9.]+).*" "\\1" LINK_VERSION ${LD_V_OUTPUT}) | ||
| endif() | ||
| set(${variable} ${LINK_VERSION} PARENT_SCOPE) | ||
| endfunction() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,248 @@ | ||
| //===-- FIROpPatterns.h -- FIR operation conversion patterns ----*- C++ -*-===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef FORTRAN_OPTIMIZER_CODEGEN_FIROPPATTERNS_H | ||
| #define FORTRAN_OPTIMIZER_CODEGEN_FIROPPATTERNS_H | ||
|
|
||
| #include "flang/Optimizer/CodeGen/TypeConverter.h" | ||
| #include "mlir/Conversion/LLVMCommon/Pattern.h" | ||
| #include "mlir/Dialect/LLVMIR/LLVMDialect.h" | ||
|
|
||
| namespace fir { | ||
|
|
||
| struct FIRToLLVMPassOptions; | ||
|
|
||
| static constexpr unsigned defaultAddressSpace = 0u; | ||
|
|
||
| class ConvertFIRToLLVMPattern : public mlir::ConvertToLLVMPattern { | ||
| public: | ||
| ConvertFIRToLLVMPattern(llvm::StringRef rootOpName, | ||
| mlir::MLIRContext *context, | ||
| const fir::LLVMTypeConverter &typeConverter, | ||
| const fir::FIRToLLVMPassOptions &options, | ||
| mlir::PatternBenefit benefit = 1); | ||
|
|
||
| protected: | ||
| mlir::Type convertType(mlir::Type ty) const { | ||
| return lowerTy().convertType(ty); | ||
| } | ||
|
|
||
| // Convert FIR type to LLVM without turning fir.box<T> into memory | ||
| // reference. | ||
| mlir::Type convertObjectType(mlir::Type firType) const; | ||
|
|
||
| mlir::LLVM::ConstantOp | ||
| genI32Constant(mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, | ||
| int value) const; | ||
|
|
||
| mlir::LLVM::ConstantOp | ||
| genConstantOffset(mlir::Location loc, | ||
| mlir::ConversionPatternRewriter &rewriter, | ||
| int offset) const; | ||
|
|
||
| /// Perform an extension or truncation as needed on an integer value. Lowering | ||
| /// to the specific target may involve some sign-extending or truncation of | ||
| /// values, particularly to fit them from abstract box types to the | ||
| /// appropriate reified structures. | ||
| mlir::Value integerCast(mlir::Location loc, | ||
| mlir::ConversionPatternRewriter &rewriter, | ||
| mlir::Type ty, mlir::Value val) const; | ||
| struct TypePair { | ||
| mlir::Type fir; | ||
| mlir::Type llvm; | ||
| }; | ||
|
|
||
| TypePair getBoxTypePair(mlir::Type firBoxTy) const; | ||
|
|
||
| /// Construct code sequence to extract the specific value from a `fir.box`. | ||
| mlir::Value getValueFromBox(mlir::Location loc, TypePair boxTy, | ||
| mlir::Value box, mlir::Type resultTy, | ||
| mlir::ConversionPatternRewriter &rewriter, | ||
| int boxValue) const; | ||
|
|
||
| /// Method to construct code sequence to get the triple for dimension `dim` | ||
| /// from a box. | ||
| llvm::SmallVector<mlir::Value, 3> | ||
| getDimsFromBox(mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys, | ||
| TypePair boxTy, mlir::Value box, mlir::Value dim, | ||
| mlir::ConversionPatternRewriter &rewriter) const; | ||
|
|
||
| llvm::SmallVector<mlir::Value, 3> | ||
| getDimsFromBox(mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys, | ||
| TypePair boxTy, mlir::Value box, int dim, | ||
| mlir::ConversionPatternRewriter &rewriter) const; | ||
|
|
||
| mlir::Value | ||
| loadDimFieldFromBox(mlir::Location loc, TypePair boxTy, mlir::Value box, | ||
| mlir::Value dim, int off, mlir::Type ty, | ||
| mlir::ConversionPatternRewriter &rewriter) const; | ||
|
|
||
| mlir::Value | ||
| getDimFieldFromBox(mlir::Location loc, TypePair boxTy, mlir::Value box, | ||
| int dim, int off, mlir::Type ty, | ||
| mlir::ConversionPatternRewriter &rewriter) const; | ||
|
|
||
| mlir::Value getStrideFromBox(mlir::Location loc, TypePair boxTy, | ||
| mlir::Value box, unsigned dim, | ||
| mlir::ConversionPatternRewriter &rewriter) const; | ||
|
|
||
| /// Read base address from a fir.box. Returned address has type ty. | ||
| mlir::Value | ||
| getBaseAddrFromBox(mlir::Location loc, TypePair boxTy, mlir::Value box, | ||
| mlir::ConversionPatternRewriter &rewriter) const; | ||
|
|
||
| mlir::Value | ||
| getElementSizeFromBox(mlir::Location loc, mlir::Type resultTy, TypePair boxTy, | ||
| mlir::Value box, | ||
| mlir::ConversionPatternRewriter &rewriter) const; | ||
|
|
||
| // Get the element type given an LLVM type that is of the form | ||
| // (array|struct|vector)+ and the provided indexes. | ||
| mlir::Type getBoxEleTy(mlir::Type type, | ||
| llvm::ArrayRef<std::int64_t> indexes) const; | ||
|
|
||
| // Return LLVM type of the object described by a fir.box of \p boxType. | ||
| mlir::Type getLlvmObjectTypeFromBoxType(mlir::Type boxType) const; | ||
|
|
||
| /// Read the address of the type descriptor from a box. | ||
| mlir::Value | ||
| loadTypeDescAddress(mlir::Location loc, TypePair boxTy, mlir::Value box, | ||
| mlir::ConversionPatternRewriter &rewriter) const; | ||
|
|
||
| // Load the attribute from the \p box and perform a check against \p maskValue | ||
| // The final comparison is implemented as `(attribute & maskValue) != 0`. | ||
| mlir::Value genBoxAttributeCheck(mlir::Location loc, TypePair boxTy, | ||
| mlir::Value box, | ||
| mlir::ConversionPatternRewriter &rewriter, | ||
| unsigned maskValue) const; | ||
|
|
||
| template <typename... ARGS> | ||
| mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty, | ||
| mlir::ConversionPatternRewriter &rewriter, | ||
| mlir::Value base, ARGS... args) const { | ||
| llvm::SmallVector<mlir::LLVM::GEPArg> cv = {args...}; | ||
| auto llvmPtrTy = | ||
| mlir::LLVM::LLVMPointerType::get(ty.getContext(), /*addressSpace=*/0); | ||
| return rewriter.create<mlir::LLVM::GEPOp>(loc, llvmPtrTy, ty, base, cv); | ||
| } | ||
|
|
||
| // Find the Block in which the alloca should be inserted. | ||
| // The order to recursively find the proper block: | ||
| // 1. An OpenMP Op that will be outlined. | ||
| // 2. A LLVMFuncOp | ||
| // 3. The first ancestor that is an OpenMP Op or a LLVMFuncOp | ||
| mlir::Block *getBlockForAllocaInsert(mlir::Operation *op) const; | ||
|
|
||
| // Generate an alloca of size 1 for an object of type \p llvmObjectTy in the | ||
| // allocation address space provided for the architecture in the DataLayout | ||
| // specification. If the address space is different from the devices | ||
| // program address space we perform a cast. In the case of most architectures | ||
| // the program and allocation address space will be the default of 0 and no | ||
| // cast will be emitted. | ||
| mlir::Value | ||
| genAllocaAndAddrCastWithType(mlir::Location loc, mlir::Type llvmObjectTy, | ||
| unsigned alignment, | ||
| mlir::ConversionPatternRewriter &rewriter) const; | ||
|
|
||
| const fir::LLVMTypeConverter &lowerTy() const { | ||
| return *static_cast<const fir::LLVMTypeConverter *>( | ||
| this->getTypeConverter()); | ||
| } | ||
|
|
||
| void attachTBAATag(mlir::LLVM::AliasAnalysisOpInterface op, | ||
| mlir::Type baseFIRType, mlir::Type accessFIRType, | ||
| mlir::LLVM::GEPOp gep) const { | ||
| lowerTy().attachTBAATag(op, baseFIRType, accessFIRType, gep); | ||
| } | ||
|
|
||
| unsigned | ||
| getAllocaAddressSpace(mlir::ConversionPatternRewriter &rewriter) const; | ||
|
|
||
| unsigned | ||
| getProgramAddressSpace(mlir::ConversionPatternRewriter &rewriter) const; | ||
|
|
||
| const fir::FIRToLLVMPassOptions &options; | ||
|
|
||
| using ConvertToLLVMPattern::match; | ||
| using ConvertToLLVMPattern::matchAndRewrite; | ||
| }; | ||
|
|
||
| template <typename SourceOp> | ||
| class FIROpConversion : public ConvertFIRToLLVMPattern { | ||
| public: | ||
| using OpAdaptor = typename SourceOp::Adaptor; | ||
|
|
||
| explicit FIROpConversion(const LLVMTypeConverter &typeConverter, | ||
| const fir::FIRToLLVMPassOptions &options, | ||
| mlir::PatternBenefit benefit = 1) | ||
| : ConvertFIRToLLVMPattern(SourceOp::getOperationName(), | ||
| &typeConverter.getContext(), typeConverter, | ||
| options, benefit) {} | ||
|
|
||
| /// Wrappers around the RewritePattern methods that pass the derived op type. | ||
| void rewrite(mlir::Operation *op, mlir::ArrayRef<mlir::Value> operands, | ||
| mlir::ConversionPatternRewriter &rewriter) const final { | ||
| rewrite(mlir::cast<SourceOp>(op), | ||
| OpAdaptor(operands, mlir::cast<SourceOp>(op)), rewriter); | ||
| } | ||
| mlir::LogicalResult match(mlir::Operation *op) const final { | ||
| return match(mlir::cast<SourceOp>(op)); | ||
| } | ||
| mlir::LogicalResult | ||
| matchAndRewrite(mlir::Operation *op, mlir::ArrayRef<mlir::Value> operands, | ||
| mlir::ConversionPatternRewriter &rewriter) const final { | ||
| return matchAndRewrite(mlir::cast<SourceOp>(op), | ||
| OpAdaptor(operands, mlir::cast<SourceOp>(op)), | ||
| rewriter); | ||
| } | ||
|
|
||
| /// Rewrite and Match methods that operate on the SourceOp type. These must be | ||
| /// overridden by the derived pattern class. | ||
| virtual mlir::LogicalResult match(SourceOp op) const { | ||
| llvm_unreachable("must override match or matchAndRewrite"); | ||
| } | ||
| virtual void rewrite(SourceOp op, OpAdaptor adaptor, | ||
| mlir::ConversionPatternRewriter &rewriter) const { | ||
| llvm_unreachable("must override rewrite or matchAndRewrite"); | ||
| } | ||
| virtual mlir::LogicalResult | ||
| matchAndRewrite(SourceOp op, OpAdaptor adaptor, | ||
| mlir::ConversionPatternRewriter &rewriter) const { | ||
| if (mlir::failed(match(op))) | ||
| return mlir::failure(); | ||
| rewrite(op, adaptor, rewriter); | ||
| return mlir::success(); | ||
| } | ||
|
|
||
| private: | ||
| using ConvertFIRToLLVMPattern::matchAndRewrite; | ||
| using ConvertToLLVMPattern::match; | ||
| }; | ||
|
|
||
| /// FIR conversion pattern template | ||
| template <typename FromOp> | ||
| class FIROpAndTypeConversion : public FIROpConversion<FromOp> { | ||
| public: | ||
| using FIROpConversion<FromOp>::FIROpConversion; | ||
| using OpAdaptor = typename FromOp::Adaptor; | ||
|
|
||
| mlir::LogicalResult | ||
| matchAndRewrite(FromOp op, OpAdaptor adaptor, | ||
| mlir::ConversionPatternRewriter &rewriter) const final { | ||
| mlir::Type ty = this->convertType(op.getType()); | ||
| return doRewrite(op, ty, adaptor, rewriter); | ||
| } | ||
|
|
||
| virtual mlir::LogicalResult | ||
| doRewrite(FromOp addr, mlir::Type ty, OpAdaptor adaptor, | ||
| mlir::ConversionPatternRewriter &rewriter) const = 0; | ||
| }; | ||
|
|
||
| } // namespace fir | ||
|
|
||
| #endif // FORTRAN_OPTIMIZER_CODEGEN_FIROPPATTERNS_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,315 @@ | ||
| //===-- CodeGen.cpp -- bridge to lower to LLVM ----------------------------===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
| // | ||
| // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "flang/Optimizer/CodeGen/FIROpPatterns.h" | ||
| #include "mlir/Dialect/OpenMP/OpenMPDialect.h" | ||
| #include "llvm/Support/Debug.h" | ||
|
|
||
| static inline mlir::Type getLlvmPtrType(mlir::MLIRContext *context, | ||
| unsigned addressSpace = 0) { | ||
| return mlir::LLVM::LLVMPointerType::get(context, addressSpace); | ||
| } | ||
|
|
||
| static unsigned getTypeDescFieldId(mlir::Type ty) { | ||
| auto isArray = fir::dyn_cast_ptrOrBoxEleTy(ty).isa<fir::SequenceType>(); | ||
| return isArray ? kOptTypePtrPosInBox : kDimsPosInBox; | ||
| } | ||
|
|
||
| namespace fir { | ||
|
|
||
| ConvertFIRToLLVMPattern::ConvertFIRToLLVMPattern( | ||
| llvm::StringRef rootOpName, mlir::MLIRContext *context, | ||
| const fir::LLVMTypeConverter &typeConverter, | ||
| const fir::FIRToLLVMPassOptions &options, mlir::PatternBenefit benefit) | ||
| : ConvertToLLVMPattern(rootOpName, context, typeConverter, benefit), | ||
| options(options) {} | ||
|
|
||
| // Convert FIR type to LLVM without turning fir.box<T> into memory | ||
| // reference. | ||
| mlir::Type | ||
| ConvertFIRToLLVMPattern::convertObjectType(mlir::Type firType) const { | ||
| if (auto boxTy = firType.dyn_cast<fir::BaseBoxType>()) | ||
| return lowerTy().convertBoxTypeAsStruct(boxTy); | ||
| return lowerTy().convertType(firType); | ||
| } | ||
|
|
||
| mlir::LLVM::ConstantOp ConvertFIRToLLVMPattern::genI32Constant( | ||
| mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, | ||
| int value) const { | ||
| mlir::Type i32Ty = rewriter.getI32Type(); | ||
| mlir::IntegerAttr attr = rewriter.getI32IntegerAttr(value); | ||
| return rewriter.create<mlir::LLVM::ConstantOp>(loc, i32Ty, attr); | ||
| } | ||
|
|
||
| mlir::LLVM::ConstantOp ConvertFIRToLLVMPattern::genConstantOffset( | ||
| mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, | ||
| int offset) const { | ||
| mlir::Type ity = lowerTy().offsetType(); | ||
| mlir::IntegerAttr cattr = rewriter.getI32IntegerAttr(offset); | ||
| return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr); | ||
| } | ||
|
|
||
| /// Perform an extension or truncation as needed on an integer value. Lowering | ||
| /// to the specific target may involve some sign-extending or truncation of | ||
| /// values, particularly to fit them from abstract box types to the | ||
| /// appropriate reified structures. | ||
| mlir::Value | ||
| ConvertFIRToLLVMPattern::integerCast(mlir::Location loc, | ||
| mlir::ConversionPatternRewriter &rewriter, | ||
| mlir::Type ty, mlir::Value val) const { | ||
| auto valTy = val.getType(); | ||
| // If the value was not yet lowered, lower its type so that it can | ||
| // be used in getPrimitiveTypeSizeInBits. | ||
| if (!valTy.isa<mlir::IntegerType>()) | ||
| valTy = convertType(valTy); | ||
| auto toSize = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); | ||
| auto fromSize = mlir::LLVM::getPrimitiveTypeSizeInBits(valTy); | ||
| if (toSize < fromSize) | ||
| return rewriter.create<mlir::LLVM::TruncOp>(loc, ty, val); | ||
| if (toSize > fromSize) | ||
| return rewriter.create<mlir::LLVM::SExtOp>(loc, ty, val); | ||
| return val; | ||
| } | ||
|
|
||
| fir::ConvertFIRToLLVMPattern::TypePair | ||
| ConvertFIRToLLVMPattern::getBoxTypePair(mlir::Type firBoxTy) const { | ||
| mlir::Type llvmBoxTy = | ||
| lowerTy().convertBoxTypeAsStruct(mlir::cast<fir::BaseBoxType>(firBoxTy)); | ||
| return TypePair{firBoxTy, llvmBoxTy}; | ||
| } | ||
|
|
||
| /// Construct code sequence to extract the specific value from a `fir.box`. | ||
| mlir::Value ConvertFIRToLLVMPattern::getValueFromBox( | ||
| mlir::Location loc, TypePair boxTy, mlir::Value box, mlir::Type resultTy, | ||
| mlir::ConversionPatternRewriter &rewriter, int boxValue) const { | ||
| if (box.getType().isa<mlir::LLVM::LLVMPointerType>()) { | ||
| auto pty = getLlvmPtrType(resultTy.getContext()); | ||
| auto p = rewriter.create<mlir::LLVM::GEPOp>( | ||
| loc, pty, boxTy.llvm, box, | ||
| llvm::ArrayRef<mlir::LLVM::GEPArg>{0, boxValue}); | ||
| auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p); | ||
| attachTBAATag(loadOp, boxTy.fir, nullptr, p); | ||
| return loadOp; | ||
| } | ||
| return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, box, boxValue); | ||
| } | ||
|
|
||
| /// Method to construct code sequence to get the triple for dimension `dim` | ||
| /// from a box. | ||
| llvm::SmallVector<mlir::Value, 3> ConvertFIRToLLVMPattern::getDimsFromBox( | ||
| mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys, TypePair boxTy, | ||
| mlir::Value box, mlir::Value dim, | ||
| mlir::ConversionPatternRewriter &rewriter) const { | ||
| mlir::Value l0 = | ||
| loadDimFieldFromBox(loc, boxTy, box, dim, 0, retTys[0], rewriter); | ||
| mlir::Value l1 = | ||
| loadDimFieldFromBox(loc, boxTy, box, dim, 1, retTys[1], rewriter); | ||
| mlir::Value l2 = | ||
| loadDimFieldFromBox(loc, boxTy, box, dim, 2, retTys[2], rewriter); | ||
| return {l0, l1, l2}; | ||
| } | ||
|
|
||
| llvm::SmallVector<mlir::Value, 3> ConvertFIRToLLVMPattern::getDimsFromBox( | ||
| mlir::Location loc, llvm::ArrayRef<mlir::Type> retTys, TypePair boxTy, | ||
| mlir::Value box, int dim, mlir::ConversionPatternRewriter &rewriter) const { | ||
| mlir::Value l0 = | ||
| getDimFieldFromBox(loc, boxTy, box, dim, 0, retTys[0], rewriter); | ||
| mlir::Value l1 = | ||
| getDimFieldFromBox(loc, boxTy, box, dim, 1, retTys[1], rewriter); | ||
| mlir::Value l2 = | ||
| getDimFieldFromBox(loc, boxTy, box, dim, 2, retTys[2], rewriter); | ||
| return {l0, l1, l2}; | ||
| } | ||
|
|
||
| mlir::Value ConvertFIRToLLVMPattern::loadDimFieldFromBox( | ||
| mlir::Location loc, TypePair boxTy, mlir::Value box, mlir::Value dim, | ||
| int off, mlir::Type ty, mlir::ConversionPatternRewriter &rewriter) const { | ||
| assert(box.getType().isa<mlir::LLVM::LLVMPointerType>() && | ||
| "descriptor inquiry with runtime dim can only be done on descriptor " | ||
| "in memory"); | ||
| mlir::LLVM::GEPOp p = genGEP(loc, boxTy.llvm, rewriter, box, 0, | ||
| static_cast<int>(kDimsPosInBox), dim, off); | ||
| auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); | ||
| attachTBAATag(loadOp, boxTy.fir, nullptr, p); | ||
| return loadOp; | ||
| } | ||
|
|
||
| mlir::Value ConvertFIRToLLVMPattern::getDimFieldFromBox( | ||
| mlir::Location loc, TypePair boxTy, mlir::Value box, int dim, int off, | ||
| mlir::Type ty, mlir::ConversionPatternRewriter &rewriter) const { | ||
| if (box.getType().isa<mlir::LLVM::LLVMPointerType>()) { | ||
| mlir::LLVM::GEPOp p = genGEP(loc, boxTy.llvm, rewriter, box, 0, | ||
| static_cast<int>(kDimsPosInBox), dim, off); | ||
| auto loadOp = rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); | ||
| attachTBAATag(loadOp, boxTy.fir, nullptr, p); | ||
| return loadOp; | ||
| } | ||
| return rewriter.create<mlir::LLVM::ExtractValueOp>( | ||
| loc, box, llvm::ArrayRef<std::int64_t>{kDimsPosInBox, dim, off}); | ||
| } | ||
|
|
||
| mlir::Value ConvertFIRToLLVMPattern::getStrideFromBox( | ||
| mlir::Location loc, TypePair boxTy, mlir::Value box, unsigned dim, | ||
| mlir::ConversionPatternRewriter &rewriter) const { | ||
| auto idxTy = lowerTy().indexType(); | ||
| return getDimFieldFromBox(loc, boxTy, box, dim, kDimStridePos, idxTy, | ||
| rewriter); | ||
| } | ||
|
|
||
| /// Read base address from a fir.box. Returned address has type ty. | ||
| mlir::Value ConvertFIRToLLVMPattern::getBaseAddrFromBox( | ||
| mlir::Location loc, TypePair boxTy, mlir::Value box, | ||
| mlir::ConversionPatternRewriter &rewriter) const { | ||
| mlir::Type resultTy = ::getLlvmPtrType(boxTy.llvm.getContext()); | ||
| return getValueFromBox(loc, boxTy, box, resultTy, rewriter, kAddrPosInBox); | ||
| } | ||
|
|
||
| mlir::Value ConvertFIRToLLVMPattern::getElementSizeFromBox( | ||
| mlir::Location loc, mlir::Type resultTy, TypePair boxTy, mlir::Value box, | ||
| mlir::ConversionPatternRewriter &rewriter) const { | ||
| return getValueFromBox(loc, boxTy, box, resultTy, rewriter, kElemLenPosInBox); | ||
| } | ||
|
|
||
| // Get the element type given an LLVM type that is of the form | ||
| // (array|struct|vector)+ and the provided indexes. | ||
| mlir::Type ConvertFIRToLLVMPattern::getBoxEleTy( | ||
| mlir::Type type, llvm::ArrayRef<std::int64_t> indexes) const { | ||
| for (unsigned i : indexes) { | ||
| if (auto t = type.dyn_cast<mlir::LLVM::LLVMStructType>()) { | ||
| assert(!t.isOpaque() && i < t.getBody().size()); | ||
| type = t.getBody()[i]; | ||
| } else if (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { | ||
| type = t.getElementType(); | ||
| } else if (auto t = type.dyn_cast<mlir::VectorType>()) { | ||
| type = t.getElementType(); | ||
| } else { | ||
| fir::emitFatalError(mlir::UnknownLoc::get(type.getContext()), | ||
| "request for invalid box element type"); | ||
| } | ||
| } | ||
| return type; | ||
| } | ||
|
|
||
| // Return LLVM type of the object described by a fir.box of \p boxType. | ||
| mlir::Type ConvertFIRToLLVMPattern::getLlvmObjectTypeFromBoxType( | ||
| mlir::Type boxType) const { | ||
| mlir::Type objectType = fir::dyn_cast_ptrOrBoxEleTy(boxType); | ||
| assert(objectType && "boxType must be a box type"); | ||
| return this->convertType(objectType); | ||
| } | ||
|
|
||
| /// Read the address of the type descriptor from a box. | ||
| mlir::Value ConvertFIRToLLVMPattern::loadTypeDescAddress( | ||
| mlir::Location loc, TypePair boxTy, mlir::Value box, | ||
| mlir::ConversionPatternRewriter &rewriter) const { | ||
| unsigned typeDescFieldId = getTypeDescFieldId(boxTy.fir); | ||
| mlir::Type tdescType = lowerTy().convertTypeDescType(rewriter.getContext()); | ||
| return getValueFromBox(loc, boxTy, box, tdescType, rewriter, typeDescFieldId); | ||
| } | ||
|
|
||
| // Load the attribute from the \p box and perform a check against \p maskValue | ||
| // The final comparison is implemented as `(attribute & maskValue) != 0`. | ||
| mlir::Value ConvertFIRToLLVMPattern::genBoxAttributeCheck( | ||
| mlir::Location loc, TypePair boxTy, mlir::Value box, | ||
| mlir::ConversionPatternRewriter &rewriter, unsigned maskValue) const { | ||
| mlir::Type attrTy = rewriter.getI32Type(); | ||
| mlir::Value attribute = | ||
| getValueFromBox(loc, boxTy, box, attrTy, rewriter, kAttributePosInBox); | ||
| mlir::LLVM::ConstantOp attrMask = genConstantOffset(loc, rewriter, maskValue); | ||
| auto maskRes = | ||
| rewriter.create<mlir::LLVM::AndOp>(loc, attrTy, attribute, attrMask); | ||
| mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); | ||
| return rewriter.create<mlir::LLVM::ICmpOp>(loc, mlir::LLVM::ICmpPredicate::ne, | ||
| maskRes, c0); | ||
| } | ||
|
|
||
| // Find the Block in which the alloca should be inserted. | ||
| // The order to recursively find the proper block: | ||
| // 1. An OpenMP Op that will be outlined. | ||
| // 2. A LLVMFuncOp | ||
| // 3. The first ancestor that is an OpenMP Op or a LLVMFuncOp | ||
| mlir::Block * | ||
| ConvertFIRToLLVMPattern::getBlockForAllocaInsert(mlir::Operation *op) const { | ||
| if (auto iface = mlir::dyn_cast<mlir::omp::OutlineableOpenMPOpInterface>(op)) | ||
| return iface.getAllocaBlock(); | ||
| if (auto llvmFuncOp = mlir::dyn_cast<mlir::LLVM::LLVMFuncOp>(op)) | ||
| return &llvmFuncOp.front(); | ||
| return getBlockForAllocaInsert(op->getParentOp()); | ||
| } | ||
|
|
||
| // Generate an alloca of size 1 for an object of type \p llvmObjectTy in the | ||
| // allocation address space provided for the architecture in the DataLayout | ||
| // specification. If the address space is different from the devices | ||
| // program address space we perform a cast. In the case of most architectures | ||
| // the program and allocation address space will be the default of 0 and no | ||
| // cast will be emitted. | ||
| mlir::Value ConvertFIRToLLVMPattern::genAllocaAndAddrCastWithType( | ||
| mlir::Location loc, mlir::Type llvmObjectTy, unsigned alignment, | ||
| mlir::ConversionPatternRewriter &rewriter) const { | ||
| auto thisPt = rewriter.saveInsertionPoint(); | ||
| mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp(); | ||
| if (mlir::isa<mlir::omp::DeclareReductionOp>(parentOp)) { | ||
| // DeclareReductionOp has multiple child regions. We want to get the first | ||
| // block of whichever of those regions we are currently in | ||
| mlir::Region *parentRegion = rewriter.getInsertionBlock()->getParent(); | ||
| rewriter.setInsertionPointToStart(&parentRegion->front()); | ||
| } else { | ||
| mlir::Block *insertBlock = getBlockForAllocaInsert(parentOp); | ||
| rewriter.setInsertionPointToStart(insertBlock); | ||
| } | ||
| auto size = genI32Constant(loc, rewriter, 1); | ||
| unsigned allocaAs = getAllocaAddressSpace(rewriter); | ||
| unsigned programAs = getProgramAddressSpace(rewriter); | ||
|
|
||
| mlir::Value al = rewriter.create<mlir::LLVM::AllocaOp>( | ||
| loc, ::getLlvmPtrType(llvmObjectTy.getContext(), allocaAs), llvmObjectTy, | ||
| size, alignment); | ||
|
|
||
| // if our allocation address space, is not the same as the program address | ||
| // space, then we must emit a cast to the program address space before use. | ||
| // An example case would be on AMDGPU, where the allocation address space is | ||
| // the numeric value 5 (private), and the program address space is 0 | ||
| // (generic). | ||
| if (allocaAs != programAs) { | ||
| al = rewriter.create<mlir::LLVM::AddrSpaceCastOp>( | ||
| loc, ::getLlvmPtrType(llvmObjectTy.getContext(), programAs), al); | ||
| } | ||
|
|
||
| rewriter.restoreInsertionPoint(thisPt); | ||
| return al; | ||
| } | ||
|
|
||
| unsigned ConvertFIRToLLVMPattern::getAllocaAddressSpace( | ||
| mlir::ConversionPatternRewriter &rewriter) const { | ||
| mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp(); | ||
| assert(parentOp != nullptr && | ||
| "expected insertion block to have parent operation"); | ||
| if (auto module = parentOp->getParentOfType<mlir::ModuleOp>()) | ||
| if (mlir::Attribute addrSpace = | ||
| mlir::DataLayout(module).getAllocaMemorySpace()) | ||
| return llvm::cast<mlir::IntegerAttr>(addrSpace).getUInt(); | ||
| return defaultAddressSpace; | ||
| } | ||
|
|
||
| unsigned ConvertFIRToLLVMPattern::getProgramAddressSpace( | ||
| mlir::ConversionPatternRewriter &rewriter) const { | ||
| mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp(); | ||
| assert(parentOp != nullptr && | ||
| "expected insertion block to have parent operation"); | ||
| if (auto module = parentOp->getParentOfType<mlir::ModuleOp>()) | ||
| if (mlir::Attribute addrSpace = | ||
| mlir::DataLayout(module).getProgramMemorySpace()) | ||
| return llvm::cast<mlir::IntegerAttr>(addrSpace).getUInt(); | ||
| return defaultAddressSpace; | ||
| } | ||
|
|
||
| } // namespace fir |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| //===-- Implementation of strfromd ------------------------------*- C++ -*-===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "src/stdlib/strfromd.h" | ||
| #include "src/stdlib/str_from_util.h" | ||
|
|
||
| #include <stdarg.h> | ||
| #include <stddef.h> | ||
|
|
||
| namespace LIBC_NAMESPACE { | ||
|
|
||
| LLVM_LIBC_FUNCTION(int, strfromd, | ||
| (char *__restrict s, size_t n, const char *__restrict format, | ||
| double fp)) { | ||
| LIBC_ASSERT(s != nullptr); | ||
|
|
||
| printf_core::FormatSection section = | ||
| internal::parse_format_string(format, fp); | ||
| printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0)); | ||
| printf_core::Writer writer(&wb); | ||
|
|
||
| int result = 0; | ||
| if (section.has_conv) | ||
| result = internal::strfromfloat_convert<double>(&writer, section); | ||
| else | ||
| result = writer.write(section.raw_string); | ||
|
|
||
| if (result < 0) | ||
| return result; | ||
|
|
||
| if (n > 0) | ||
| wb.buff[wb.buff_cur] = '\0'; | ||
|
|
||
| return writer.get_chars_written(); | ||
| } | ||
|
|
||
| } // namespace LIBC_NAMESPACE |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| //===-- Implementation header for strfromd ------------------------*- C++--===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_LIBC_SRC_STDLIB_STRFROMD_H | ||
| #define LLVM_LIBC_SRC_STDLIB_STRFROMD_H | ||
|
|
||
| #include <stddef.h> | ||
|
|
||
| namespace LIBC_NAMESPACE { | ||
|
|
||
| int strfromd(char *__restrict s, size_t n, const char *__restrict format, | ||
| double fp); | ||
|
|
||
| } // namespace LIBC_NAMESPACE | ||
|
|
||
| #endif // LLVM_LIBC_SRC_STDLIB_STRFROMD_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| //===-- Implementation of strfroml ------------------------------*- C++ -*-===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "src/stdlib/strfroml.h" | ||
| #include "src/stdlib/str_from_util.h" | ||
|
|
||
| #include <stdarg.h> | ||
| #include <stddef.h> | ||
|
|
||
| namespace LIBC_NAMESPACE { | ||
|
|
||
| LLVM_LIBC_FUNCTION(int, strfroml, | ||
| (char *__restrict s, size_t n, const char *__restrict format, | ||
| long double fp)) { | ||
| LIBC_ASSERT(s != nullptr); | ||
|
|
||
| printf_core::FormatSection section = | ||
| internal::parse_format_string(format, fp); | ||
|
|
||
| // To ensure that the conversion function actually uses long double, | ||
| // the length modifier has to be set to LenghtModifier::L | ||
| section.length_modifier = printf_core::LengthModifier::L; | ||
|
|
||
| printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0)); | ||
| printf_core::Writer writer(&wb); | ||
|
|
||
| int result = 0; | ||
| if (section.has_conv) | ||
| result = internal::strfromfloat_convert<long double>(&writer, section); | ||
| else | ||
| result = writer.write(section.raw_string); | ||
|
|
||
| if (result < 0) | ||
| return result; | ||
|
|
||
| if (n > 0) | ||
| wb.buff[wb.buff_cur] = '\0'; | ||
|
|
||
| return writer.get_chars_written(); | ||
| } | ||
|
|
||
| } // namespace LIBC_NAMESPACE |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| //===-- Implementation header for strfroml ------------------------*- C++--===// | ||
| // | ||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | ||
| // See https://llvm.org/LICENSE.txt for license information. | ||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
| // | ||
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #ifndef LLVM_LIBC_SRC_STDLIB_STRFROML_H | ||
| #define LLVM_LIBC_SRC_STDLIB_STRFROML_H | ||
|
|
||
| #include <stddef.h> | ||
|
|
||
| namespace LIBC_NAMESPACE { | ||
|
|
||
| int strfroml(char *__restrict s, size_t n, const char *__restrict format, | ||
| long double fp); | ||
|
|
||
| } // namespace LIBC_NAMESPACE | ||
|
|
||
| #endif // LLVM_LIBC_SRC_STDLIB_STRFROML_H |