Index: flang/lib/Optimizer/Dialect/FIRDialect.cpp =================================================================== --- flang/lib/Optimizer/Dialect/FIRDialect.cpp +++ flang/lib/Optimizer/Dialect/FIRDialect.cpp @@ -26,7 +26,7 @@ bool isLegalToInline(mlir::Operation *call, mlir::Operation *callable, bool wouldBeCloned) const final { - return false; //fir::canLegallyInline(call, callable, wouldBeCloned); + return fir::canLegallyInline(call, callable, wouldBeCloned); } /// This hook checks to see if the operation `op` is legal to inline into the @@ -34,7 +34,7 @@ bool isLegalToInline(mlir::Operation *op, mlir::Region *reg, bool wouldBeCloned, mlir::BlockAndValueMapping &map) const final { - return false; //fir::canLegallyInline(op, reg, wouldBeCloned, map); + return fir::canLegallyInline(op, reg, wouldBeCloned, map); } /// This hook is called when a terminator operation has been inlined. Index: flang/test/Fir/addrof.1.fir =================================================================== --- /dev/null +++ flang/test/Fir/addrof.1.fir @@ -0,0 +1,12 @@ +// RUN: tco -emit-fir %s | tco | FileCheck %s + +// CHECK: @var_x = external global i32 +fir.global @var_x : !fir.int<4> {} + +// CHECK-LABEL: define i32* @getAddressOfX +func @getAddressOfX() -> !fir.ref> { + %1 = fir.address_of(@var_x) : !fir.ref> + // CHECK: ret i32* @var_x + return %1 : !fir.ref> +} + Index: flang/test/Fir/alloc.fir =================================================================== --- /dev/null +++ flang/test/Fir/alloc.fir @@ -0,0 +1,31 @@ +// RUN: tco -emit-fir %s | tco | FileCheck %s + +// CHECK-LABEL: define i32* @f1() +func @f1() -> !fir.ref { + // CHECK: alloca i32, i64 1 + %1 = fir.alloca i32 + return %1 : !fir.ref +} + +// CHECK-LABEL: define i32* @f2() +func @f2() -> !fir.ref { + %0 = constant 100 : index + // CHECK: alloca i32, i64 100 + %1 = fir.alloca i32, %0 + return %1 : !fir.ref +} + +// CHECK-LABEL: define i32* @f3() +func @f3() -> !fir.heap { + // CHECK: call i8* @malloc(i64 4) + %1 = fir.allocmem i32 + return %1 : !fir.heap +} + +// CHECK-LABEL: define i32* @f4() +func @f4() -> !fir.heap { + %0 = constant 100 : index + // CHECK: call i8* @malloc(i64 400) + %1 = fir.allocmem i32, %0 + return %1 : !fir.heap +} Index: flang/test/Fir/box.fir =================================================================== --- /dev/null +++ flang/test/Fir/box.fir @@ -0,0 +1,125 @@ +// RUN: tco -o - %s | FileCheck %s + +// Global box initialization (test must come first because llvm globals are emitted first). +// CHECK-LABEL: @globalx = internal global { i32*, i64, i32, i8, i8, i8, i8 } { i32* null, i64 4, i32 20180515, i8 0, i8 9, i8 2, i8 0 } +fir.global internal @globalx : !fir.box> { + %c0 = constant 0 : index + %0 = fir.convert %c0 : (index) -> !fir.heap + %1 = fir.embox %0 : (!fir.heap) -> !fir.box> + fir.has_value %1 : !fir.box> +} + +// CHECK-LABEL: @globaly = internal global { float*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } { float* null, i64 4, i32 20180515, i8 1, i8 25, i8 2, i8 0,{{.*}}[3 x i64] [i64 1, i64 0, i64 4] +fir.global internal @globaly : !fir.box>> { + %c0 = constant 0 : index + %0 = fir.convert %c0 : (index) -> !fir.heap> + %1 = fir.shape %c0 : (index) -> !fir.shape<1> + %2 = fir.embox %0(%1) : (!fir.heap>, !fir.shape<1>) -> !fir.box>> + fir.has_value %2 : !fir.box>> +} + +// CHECK-LABEL: declare void @g({ float*, i64, i32, i8, i8, i8, i8 }*) +func private @g(%b : !fir.box) +// CHECK-LABEL: declare void @ga({ float*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }*) +func private @ga(%b : !fir.box>) + +// CHECK-LABEL: define void @f +// CHECK: (float* %[[ARG:.*]]) +func @f(%a : !fir.ref) { + // CHECK: %[[DESC:.*]] = alloca { float*, i64, i32, i8, i8, i8, i8 } + // CHECK: %[[INS0:.*]] = insertvalue {{.*}} undef, float* %[[ARG]], 0 + // CHECK: %[[INS1:.*]] = insertvalue {{.*}} %[[INS0]], i64 4, 1 + // CHECK: %[[INS2:.*]] = insertvalue {{.*}} %[[INS1]], i32 {{.*}}, 2 + // CHECK: %[[INS3:.*]] = insertvalue {{.*}} %[[INS2]], i8 0, 3 + // CHECK: %[[INS4:.*]] = insertvalue {{.*}} %[[INS3]], i8 25, 4 + // CHECK: %[[INS5:.*]] = insertvalue {{.*}} %[[INS4]], i8 0, 5 + // CHECK: %[[INS6:.*]] = insertvalue {{.*}} %[[INS5]], i8 0, 6 + // CHECK: store {{.*}} %[[INS6]], {{.*}}* %[[DESC]] + %b = fir.embox %a : (!fir.ref) -> !fir.box + + // CHECK: call void @g({{.*}} %[[DESC]]) + fir.call @g(%b) : (!fir.box) -> () + // CHECK: ret void + return +} + +// CHECK-LABEL: define void @fa +// CHECK: ([100 x float]* %[[ARG:.*]]) +func @fa(%a : !fir.ref>) { + %c = fir.convert %a : (!fir.ref>) -> !fir.ref> + %c1 = constant 1 : index + %c100 = constant 100 : index + %d = fir.shape %c100 : (index) -> !fir.shape<1> + // CHECK: %[[INS70:.*]] = insertvalue {{.*}} %{{.*}}, i64 0, 7, 0, 0 + // CHECK: %[[INS71:.*]] = insertvalue {{.*}} %[[INS70]], i64 100, 7, 0, 1 + // CHECK: %[[INS72:.*]] = insertvalue {{.*}} %[[INS71]], i64 4, 7, 0, 2 + %b = fir.embox %c(%d) : (!fir.ref>, !fir.shape<1>) -> !fir.box> + // CHECK: call void @ga( + fir.call @ga(%b) : (!fir.box>) -> () + // CHECK: ret void + return +} + +// Boxing of a scalar character of dynamic length +// CHECK-LABEL: define { i8*, i64, i32, i8, i8, i8, i8 }* @b1( +// CHECK-SAME: i8* %[[arg0:.*]], i64 %[[arg1:.*]]) +func @b1(%arg0 : !fir.ref>, %arg1 : index) -> !fir.box> { + // CHECK: insertvalue {{.*}} i8* %[[arg0]], 0 + // CHECK: insertvalue {{.*}} i64 %[[arg1]], 1 + // CHECK: insertvalue {{.*}} i32 20180515, 2 + %x = fir.embox %arg0 typeparams %arg1 : (!fir.ref>, index) -> !fir.box> + return %x : !fir.box> +} + +// Boxing of a dynamic array of character with static length (5) +// CHECK-LABEL: define { [5 x i8]*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }* @b2( +// CHECK-SAME: [5 x i8]* %[[arg0:.*]], i64 %[[arg1:.*]]) +func @b2(%arg0 : !fir.ref>>, %arg1 : index) -> !fir.box>> { + %1 = fir.shape %arg1 : (index) -> !fir.shape<1> + // CHECK: insertvalue {{.*}} [5 x i8]* %[[arg0]], 0 + // CHECK: insertvalue {{.*}} i64 5, 1 + // CHECK: insertvalue {{.*}} i32 20180515, 2 + // CHECK: insertvalue {{.*}} i64 %[[arg1]], 7, 0, 1 + // CHECK: insertvalue {{.*}} i64 5, 7, 0, 2 + %2 = fir.embox %arg0(%1) : (!fir.ref>>, !fir.shape<1>) -> !fir.box>> + return %2 : !fir.box>> +} + +// Boxing of a dynamic array of character of dynamic length +// CHECK-LABEL: define { i8*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }* @b3( +// CHECK-SAME: i8* %[[arg0:.*]], i64 %[[arg1:.*]], i64 %[[arg2:.*]]) +func @b3(%arg0 : !fir.ref>>, %arg1 : index, %arg2 : index) -> !fir.box>> { + %1 = fir.shape %arg2 : (index) -> !fir.shape<1> + // CHECK: insertvalue {{.*}} i8* %[[arg0]], 0 + // CHECK: insertvalue {{.*}} i64 %[[arg1]], 1 + // CHECK: insertvalue {{.*}} i32 20180515, 2 + // CHECK: insertvalue {{.*}} i64 %[[arg2]], 7, 0, 1 + // CHECK: insertvalue {{.*}} i64 %[[arg1]], 7, 0, 2 + %2 = fir.embox %arg0(%1) typeparams %arg1 : (!fir.ref>>, !fir.shape<1>, index) -> !fir.box>> + return %2 : !fir.box>> +} + +// Boxing of a static array of character of dynamic length +// CHECK-LABEL: define { i8*, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }* @b4( +// CHECK-SAME: i8* %[[arg0:.*]], i64 %[[arg1:.*]]) +func @b4(%arg0 : !fir.ref>>, %arg1 : index) -> !fir.box>> { + %c_7 = constant 7 : index + %1 = fir.shape %c_7 : (index) -> !fir.shape<1> + // CHECK: insertvalue {{.*}} i8* %[[arg0]], 0 + // CHECK: insertvalue {{.*}} i64 %[[arg1]], 1 + // CHECK: insertvalue {{.*}} i32 20180515, 2 + // CHECK: insertvalue {{.*}} i64 7, 7, 0, 1 + // CHECK: insertvalue {{.*}} i64 %[[arg1]], 7, 0, 2 + %x = fir.embox %arg0(%1) typeparams %arg1 : (!fir.ref>>, !fir.shape<1>, index) -> !fir.box>> + return %x : !fir.box>> +} + +// Storing a fir.box into a fir.ref (modifying descriptors). +// CHECK-LABEL: define void @b5( +// CHECK-SAME: { float*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }* %[[arg0:.*]], { float*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }* %[[arg1:.*]]) +func @b5(%arg0 : !fir.ref>>>, %arg1 : !fir.box>>) { + fir.store %arg1 to %arg0 : !fir.ref>>> + // CHECK: %[[boxLoad:.*]] = load { float*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }, { float*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }* %[[arg1]] + // CHECK: store { float*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] } %[[boxLoad]], { float*, i64, i32, i8, i8, i8, i8, [2 x [3 x i64]] }* %[[arg0]] + return +} Index: flang/test/Fir/boxchar.fir =================================================================== --- /dev/null +++ flang/test/Fir/boxchar.fir @@ -0,0 +1,22 @@ +// RUN: tco --target=x86_64-unknown-linux-gnu %s | FileCheck %s + +// Test of building and passing boxchar. + +func private @callee(%x : !fir.boxchar<1>) + +// CHECK-LABEL: define void @get_name +func @get_name() { + %1 = fir.address_of (@name) : !fir.ref> + %2 = constant 9 : i64 + %3 = fir.convert %1 : (!fir.ref>) -> !fir.ref> + %4 = fir.emboxchar %3, %2 : (!fir.ref>, i64) -> !fir.boxchar<1> + // CHECK: call void @callee(i8* getelementptr inbounds ([9 x i8], [9 x i8]* @name, i32 0, i32 0), i64 9) + fir.call @callee(%4) : (!fir.boxchar<1>) -> () + return +} + +fir.global @name constant : !fir.char<1,9> { + %str = fir.string_lit "Your name"(9) : !fir.char<1,9> + //constant 1 + fir.has_value %str : !fir.char<1,9> +} Index: flang/test/Fir/char01.fir =================================================================== --- /dev/null +++ flang/test/Fir/char01.fir @@ -0,0 +1,14 @@ +// RUN: tco -emit-fir %s | tco | FileCheck %s + +// CHECK-LABEL: @test +func @test(%arg0 : !fir.ref>, %arg1 : !fir.ref>, %arg2 : i32) { + %0 = fir.convert %arg1 : (!fir.ref>) -> !fir.ref>> + // CHECK: getelementptr [1 x i8], [1 x i8]* + %1 = fir.coordinate_of %0, %arg2 : (!fir.ref>>, i32) -> !fir.ref> + // CHECK: load [1 x i8], [1 x i8]* + %2 = fir.load %1 : !fir.ref> + // CHECK: store [1 x i8] + fir.store %2 to %arg0 : !fir.ref> + // CHECK: ret void + return +} Index: flang/test/Fir/compare.fir =================================================================== --- /dev/null +++ flang/test/Fir/compare.fir @@ -0,0 +1,45 @@ +// RUN: tco -emit-fir %s | tco --target=x86_64-unknown-linux-gnu | FileCheck %s + +// CHECK-LABEL: define i1 @cmp(x86_fp80 %0, x86_fp80 %1) +func @cmp(%a : !fir.real<10>, %b : !fir.real<10>) -> i1 { +// CHECK: fcmp oeq x86_fp80 %0, %1 + %1 = "fir.cmpf"(%a, %b) {predicate = 1} : (!fir.real<10>, !fir.real<10>) -> i1 + return %1 : i1 +} + +// CHECK-LABEL: define i1 @cmp2(fp128 %0, fp128 %1) +func @cmp2(%a : !fir.real<16>, %b : !fir.real<16>) -> i1 { + // CHECK: fcmp ult fp128 %0, %1 + %1 = fir.cmpf "ult", %a, %b : !fir.real<16> + return %1 : i1 +} + +// CHECK-LABEL: define i1 @cmp3(<2 x float> %0, <2 x float> %1) +func @cmp3(%a : !fir.complex<4>, %b : !fir.complex<4>) -> i1 { + // CHECK: fcmp oeq float + %1 = fir.cmpc "oeq", %a, %b : !fir.complex<4> + return %1 : i1 +} + +// CHECK-LABEL: define double @neg1(double %0) +func @neg1(%a : !fir.real<8>) -> !fir.real<8> { + // CHECK: fneg double %0 + %1 = "fir.negf"(%a) : (!fir.real<8>) -> !fir.real<8> + return %1 : !fir.real<8> +} + +// CHECK-LABEL: define double @neg2(double %0) +func @neg2(%a : !fir.real<8>) -> !fir.real<8> { + // CHECK: fneg double %0 + %1 = fir.negf %a : !fir.real<8> + return %1 : !fir.real<8> +} + +// CHECK-LABEL: define { double, double } @neg3(double %0, double %1) +func @neg3(%a : !fir.complex<8>) -> !fir.complex<8> { + // CHECK: %[[g2:.*]] = insertvalue { double, double } % + // CHECK: %[[r3:.*]] = fneg double + // CHECK: insertvalue { double, double } %[[g2]], double %[[r3]] + %1 = fir.negc %a : !fir.complex<8> + return %1 : !fir.complex<8> +} Index: flang/test/Fir/constant.fir =================================================================== --- /dev/null +++ flang/test/Fir/constant.fir @@ -0,0 +1,26 @@ +// RUN: tco -emit-fir %s | tco | FileCheck %s + +// CHECK-LABEL: define [3 x i8] @x +func @x() -> !fir.char<1,3> { + %1 = fir.string_lit "xyz"(3) : !fir.char<1,3> + // CHECK: ret [3 x i8] c"xyz" + return %1 : !fir.char<1,3> +} + +// CHECK-LABEL: define x86_fp80 @y() +func @y() -> !fir.real<10> { + %c1 = constant 42.4 :f32 + %0 = fir.convert %c1 : (f32) -> !fir.real<10> + // CHECK: ret x86_fp80 0xK4004A9999A0000000000 + // TODO: What's that number? + return %0 : !fir.real<10> +} + +// CHECK-LABEL: define i16 @z() +func @z() -> !fir.logical<2> { + %1 = constant true + %0 = fir.convert %1 : (i1) -> !fir.logical<2> + // CHECK-LABEL: ret i16 -1 + // TODO: Why is it -1? + return %0 : !fir.logical<2> +} Index: flang/test/Fir/convert-fold.fir =================================================================== --- /dev/null +++ flang/test/Fir/convert-fold.fir @@ -0,0 +1,28 @@ +// RUN: tco %s | FileCheck %s + +// CHECK-LABEL: @ftest +func @ftest(%x : i1) -> i1 { + // this pair of converts should be folded and DCEd + %1 = fir.convert %x : (i1) -> !fir.logical<1> + %2 = fir.convert %1 : (!fir.logical<1>) -> i1 + // CHECK-NEXT: ret i1 %0 + return %2 : i1 +} + +// CHECK-LABEL: @gtest +func @gtest(%x : !fir.logical<2>) -> !fir.logical<2> { + // this pair of converts should be folded and DCEd + %1 = fir.convert %x : (!fir.logical<2>) -> i1 + %2 = fir.convert %1 : (i1) -> !fir.logical<2> + // CHECK-NEXT: ret i16 %0 + return %2 : !fir.logical<2> +} + +// CHECK-LABEL: @htest +func @htest(%x : !fir.int<4>) -> !fir.int<4> { + // these converts are NOPs and should be folded away + %1 = fir.convert %x : (!fir.int<4>) -> !fir.int<4> + %2 = fir.convert %1 : (!fir.int<4>) -> !fir.int<4> + // CHECK-NEXT: ret i32 %0 + return %2 : !fir.int<4> +} Index: flang/test/Fir/convert.fir =================================================================== --- /dev/null +++ flang/test/Fir/convert.fir @@ -0,0 +1,13 @@ +// RUN: tco --target=x86_64-unknown-linux-gnu %s | FileCheck %s + +// CHECK-LABEL: define { double, double } @c(<2 x float> % +func @c(%x : !fir.complex<4>) -> !fir.complex<8> { +// CHECK: %[[R:.*]] = extractvalue { float, float } %{{.*}}, 0 +// CHECK: %[[I:.*]] = extractvalue { float, float } %{{.*}}, 1 +// CHECK: %[[CR:.*]] = fpext float %[[R]] to double +// CHECK: %[[CI:.*]] = fpext float %[[I]] to double +// CHECK: %[[X:.*]] = insertvalue { double, double } undef, double %[[CR]], 0 +// CHECK: insertvalue { double, double } %[[X]], double %[[CI]], 1 + %1 = fir.convert %x : (!fir.complex<4>) -> !fir.complex<8> + return %1 : !fir.complex<8> +} Index: flang/test/Fir/coordinateof.fir =================================================================== --- /dev/null +++ flang/test/Fir/coordinateof.fir @@ -0,0 +1,49 @@ +// RUN: tco -emit-fir %s | tco | FileCheck %s + +// tests on coordinate_of op + +// CHECK-LABEL: @foo1 +func @foo1(%i : i32, %j : i32, %k : i32) -> !fir.ref { + %1 = fir.alloca !fir.array<10 x 20 x 30 x f32> + // CHECK: %[[ptr:.*]] = bitcast [30 x [20 x [10 x + %2 = fir.convert %1 : (!fir.ref>) -> !fir.ref> + // CHECK: getelementptr [20 x [10 x float]], [20 x [10 x float]]* %[[ptr]] + %3 = fir.coordinate_of %2, %i, %j, %k : (!fir.ref>, i32, i32, i32) -> !fir.ref + return %3 : !fir.ref +} + +// CHECK-LABEL: @foo2 +func @foo2(%i : i32, %j : i32, %k : i32) -> !fir.ref { + %1 = fir.alloca !fir.array<10 x 20 x 30 x f32> + // CHECK: %[[ptr:.*]] = bitcast [30 x [20 x [10 x + %2 = fir.convert %1 : (!fir.ref>) -> !fir.ref> + // CHECK: getelementptr float, float* %[[ptr]] + %3 = fir.coordinate_of %2, %i : (!fir.ref>, i32) -> !fir.ref + return %3 : !fir.ref +} + +// CHECK-LABEL: @foo3 +func @foo3(%box : !fir.box>, %i : i32) -> i32 { + // CHECK: %[[cvt:.*]] = sext i32 % + %ii = fir.convert %i : (i32) -> index + // CHECK: %[[gep0:.*]] = getelementptr { i32* + // CHECK: %[[boxptr:.*]] = load i32*, i32** %[[gep0]] + // CHECK: %[[gep1:.*]] = getelementptr i32, i32* %[[boxptr]], i64 % + %1 = fir.coordinate_of %box, %ii : (!fir.box>, index) -> !fir.ref + // CHECK: load i32, i32* %[[gep1]] + %rv = fir.load %1 : !fir.ref + return %rv : i32 +} + +// CHECK-LABEL: @foo4 +func @foo4(%a : !fir.ptr>, %i : i32, %j : i64, %k : index) -> i32 { + // CHECK: getelementptr [25 x [15 x [5 x + %1 = fir.coordinate_of %a, %k : (!fir.ptr>, index) -> !fir.ref> + // CHECK: getelementptr [15 x [5 x + %2 = fir.coordinate_of %1, %j : (!fir.ref>, i64) -> !fir.ref> + // CHECK: %[[ref:.*]] = getelementptr [5 x + %3 = fir.coordinate_of %2, %i : (!fir.ref>, i32) -> !fir.ref + // CHECK: load i32, i32* %[[ref]] + %4 = fir.load %3 : !fir.ref + return %4 : i32 +} Index: flang/test/Fir/embox.fir =================================================================== --- /dev/null +++ flang/test/Fir/embox.fir @@ -0,0 +1,10 @@ +// RUN: tco -emit-fir %s | tco | FileCheck %s + +// TODO: Is test correct? +// CHECK-LABEL: define void @f(double* %0) +func @f(%arg : !fir.ref>) { + %c1000 = constant 1000 : index + %aDim = fir.shape %c1000 : (index) -> !fir.shape<1> + %2 = fir.embox %arg(%aDim) : (!fir.ref>, !fir.shape<1>) -> !fir.box> + return +} Index: flang/test/Fir/fir-ops.fir =================================================================== --- flang/test/Fir/fir-ops.fir +++ flang/test/Fir/fir-ops.fir @@ -1,7 +1,6 @@ // Test the FIR operations // RUN: tco -emit-fir %s | tco -emit-fir | FileCheck %s -// XFAIL: * // CHECK-LABEL: func private @it1() -> !fir.int<4> // CHECK: func private @box1() -> !fir.boxchar<2> Index: flang/test/Fir/global.fir =================================================================== --- /dev/null +++ flang/test/Fir/global.fir @@ -0,0 +1,34 @@ +// RUN: tco %s | FileCheck %s + +// CHECK: @g_i0 = global i32 0 +fir.global @g_i0 : i32 { + %1 = constant 0 : i32 + fir.has_value %1 : i32 +} + +// CHECK: @g_i2 = global i32 2 +fir.global @g_i2 : i32 { + %1 = constant 2 : i32 + fir.has_value %1 : i32 +} + +// CHECK: @g_ci5 = constant i32 5 +fir.global @g_ci5 constant : i32 { + %c = constant 5 : i32 + fir.has_value %c : i32 +} + +// CHECK: @i_i515 = internal global i32 515 +fir.global internal @i_i515 (515:i32) : i32 + +// CHECK: @C_i511 = common global i32 511 +fir.global common @C_i511 (511:i32) : i32 + +// CHECK: @w_i86 = weak global i32 86 +fir.global weak @w_i86 (86:i32) : i32 + +// CHECK: @str1 = global [6 x i8] c"Hello!" +fir.global @str1 : !fir.char<1,6> { + %1 = fir.string_lit "Hello!"(6) : !fir.char<1,6> + fir.has_value %1 : !fir.char<1,6> +} Index: flang/test/Fir/inline.fir =================================================================== --- /dev/null +++ flang/test/Fir/inline.fir @@ -0,0 +1,19 @@ +// RUN: tco --inline-all %s -o - | FileCheck %s + +// CHECK-LABEL: @add +func @add(%a : i32, %b : i32) -> i32 { + // CHECK: %[[add:.*]] = add i32 + %p = addi %a, %b : i32 + // CHECK: ret i32 %[[add]] + return %p : i32 +} + +// CHECK-LABEL: @test +func @test(%a : i32, %b : i32, %c : i32) -> i32 { + // CHECK: %[[add:.*]] = add i32 + %m = fir.call @add(%a, %b) : (i32, i32) -> i32 + // CHECK: %[[mul:.*]] = mul i32 %[[add]], + %n = muli %m, %c : i32 + // CHECK: ret i32 %[[mul]] + return %n : i32 +} Index: flang/test/Fir/real.fir =================================================================== --- /dev/null +++ flang/test/Fir/real.fir @@ -0,0 +1,51 @@ +// Test lowering of REAL operations from FIR to LLVM IR + +// RUN: tco %s | FileCheck %s + +// CHECK-LABEL: @bar +func @bar(%a : !fir.real<2>, %b : !fir.real<4>, %c : !fir.real<8>, %d : !fir.real<10>, %e : !fir.real<16>) -> !fir.real<10> { + // CHECK: fpext half %{{.*}} to x86_fp80 + %1 = fir.convert %a : (!fir.real<2>) -> !fir.real<10> + // CHECK: fpext float %{{.*}} to x86_fp80 + %2 = fir.convert %b : (!fir.real<4>) -> !fir.real<10> + // CHECK: fpext double %{{.*}} to x86_fp80 + %3 = fir.convert %c : (!fir.real<8>) -> !fir.real<10> + // CHECK-NOT: fpext + // CHECK-NOT: fptrunc + %4 = fir.convert %d : (!fir.real<10>) -> !fir.real<10> + // CHECK: fptrunc fp128 %{{.*}} to x86_fp80 + %5 = fir.convert %e : (!fir.real<16>) -> !fir.real<10> + // CHECK-NEXT: call x86_fp80 + %6 = call @foop(%1, %2, %3, %4, %5) : (!fir.real<10>, !fir.real<10>, !fir.real<10>, !fir.real<10>, !fir.real<10>) -> !fir.real<10> + return %6 : !fir.real<10> +} + +// CHECK-LABEL: @foo +func @foo(%a : !fir.real<16>, %b : !fir.real<16>, %c : !fir.real<16>, %d : !fir.real<16>, %e : !fir.real<16>) -> !fir.real<16> { + // CHECK: fadd fp128 + %1 = fir.addf %a, %b : !fir.real<16> + // CHECK: fmul fp128 + %2 = fir.mulf %1, %c : !fir.real<16> + // CHECK: fsub fp128 + %3 = fir.subf %2, %d : !fir.real<16> + // CHECK: fdiv fp128 + %4 = fir.divf %3, %e : !fir.real<16> + // CHECK: frem fp128 + %5 = fir.modf %4, %a : !fir.real<16> + return %5 : !fir.real<16> +} + +// CHECK-LABEL: @foop +func @foop(%a : !fir.real<10>, %b : !fir.real<10>, %c : !fir.real<10>, %d : !fir.real<10>, %e : !fir.real<10>) -> !fir.real<10> { + // CHECK: fadd x86_fp80 + %1 = fir.addf %a, %b : !fir.real<10> + // CHECK: fmul x86_fp80 + %2 = fir.mulf %1, %c : !fir.real<10> + // CHECK: fsub x86_fp80 + %3 = fir.subf %2, %d : !fir.real<10> + // CHECK: fdiv x86_fp80 + %4 = fir.divf %3, %e : !fir.real<10> + // CHECK: frem x86_fp80 + %5 = fir.modf %4, %a : !fir.real<10> + return %5 : !fir.real<10> +} Index: flang/test/Fir/recursive-type.fir =================================================================== --- /dev/null +++ flang/test/Fir/recursive-type.fir @@ -0,0 +1,11 @@ +// Test lowering FIR to LLVM IR for a recursive type + +// RUN: tco %s | FileCheck %s + +// CHECK-LABEL: %t = type { %t* } +!t = type !fir.type>}> + +// CHECK-LABEL: @a(%t %{{.*}}) +func @a(%a : !t) { + return +} Index: flang/test/Fir/select.fir =================================================================== --- /dev/null +++ flang/test/Fir/select.fir @@ -0,0 +1,63 @@ +// Test lowering FIR to LLVM IR of fir.select{|_rank|_case} + +// RUN: tco %s | FileCheck %s + +// CHECK-LABEL: @f +func @f(%a : i32) -> i32 { + %1 = constant 1 : i32 + %2 = constant 42 : i32 + // CHECK: icmp eq i32 %{{.*}}, 1 + fir.select %a : i32 [1, ^bb2(%1:i32), unit, ^bb3(%2:i32)] +^bb2(%3 : i32) : + return %3 : i32 +^bb3(%4 : i32) : + %5 = addi %4, %4 : i32 + // CHECK: ret i32 + return %5 : i32 +} + +// CHECK-LABEL: @g +func @g(%a : i32) -> i32 { + %1 = constant 1 : i32 + %2 = constant 42 : i32 + // CHECK-DAG: icmp eq i32 %{{.*}}, 1 + // CHECK-DAG: icmp eq i32 %{{.*}}, -1 + fir.select_rank %a : i32 [1, ^bb2(%1:i32), -1, ^bb4, unit, ^bb3(%2:i32)] +^bb2(%3 : i32) : + return %3 : i32 +^bb3(%4 : i32) : + %5 = addi %4, %4 : i32 + return %5 : i32 +^bb4: + // CHECK: ret i32 + return %a : i32 +} + +// CHECK-LABEL: @h +func @h(%a : i32) -> i32 { + %1 = constant 1 : i32 + %2 = constant 42 : i32 + %b1 = constant 4 : i32 + %b2 = constant 14 : i32 + %b3 = constant 82 : i32 + %b4 = constant 96 : i32 + // CHECK-DAG: icmp eq i32 %{{.*}}, 1 + // CHECK-DAG: icmp sle i32 4, %{{.*}} + // CHECK-DAG: icmp sle i32 %{{.*}}, 14 + // CHECK-DAG: icmp sle i32 82, %{{.*}} + // CHECK-DAG: icmp sle i32 %{{.*}}, 96 + fir.select_case %a : i32 [#fir.point, %1, ^bb2(%1:i32), #fir.lower, %b1, ^bb4, #fir.upper, %b2, ^bb6, #fir.interval, %b3, %b4, ^bb5, unit, ^bb3(%2:i32)] +^bb2(%3 : i32) : + return %3 : i32 +^bb3(%4 : i32) : + %5 = addi %4, %4 : i32 + br ^bb2(%5 : i32) +^bb4: + return %a : i32 +^bb5: + return %1 : i32 +^bb6: + %x = addi %b4, %b3 : i32 + // CHECK: ret i32 + return %x : i32 +} Index: flang/test/Fir/types-to-llvm.fir =================================================================== --- /dev/null +++ flang/test/Fir/types-to-llvm.fir @@ -0,0 +1,15 @@ +// RUN: tco %s | FileCheck %s + +// CHECK-LABEL: declare void @foo0([20 x [10 x [5 x i32]]]*) +func private @foo0(%arg0: !fir.ref>) +// CHECK-LABEL: declare void @foo1(i32*) +func private @foo1(%arg0: !fir.ref>) +// CHECK-LABEL: declare void @foo2(i32*) +func private @foo2(%arg0: !fir.ref>) +// CHECK-LABEL: declare void @foo3([10 x [5 x i32]]*) +func private @foo3(%arg0: !fir.ref>) +// CHECK-LABEL: declare void @foo4(i32*) +func private @foo4(%arg0: !fir.ref>) + +// CHECK-LABEL: declare void @byval5(i32*) +func private @byval5(%arg0: !fir.array) Index: flang/test/Fir/widechar.fir =================================================================== --- /dev/null +++ flang/test/Fir/widechar.fir @@ -0,0 +1,22 @@ +// RUN: tco %s | FileCheck %s + +// CHECK-LABEL: @character_literal1 +func @character_literal1() -> !fir.char<1,13> { + %0 = fir.string_lit "Hello, World!"(13) : !fir.char<1,13> + // CHECK: ret [13 x i8] c"Hello, World!" + return %0 : !fir.char<1,13> +} + +// CHECK-LABEL: @character_literal2 +func @character_literal2() -> !fir.char<2,2> { + %0 = fir.string_lit [234, 456](2) : !fir.char<2,2> + // CHECK: ret [2 x i16] [i16 234, i16 456] + return %0 : !fir.char<2,2> +} + +// CHECK-LABEL: @character_literal4 +func @character_literal4() -> !fir.char<4,3> { + %0 = fir.string_lit [89123, 999256, 4](3) : !fir.char<4,3> + // CHECK: ret [3 x i32] [i32 89123, i32 999256, i32 4] + return %0 : !fir.char<4,3> +} Index: flang/tools/tco/CMakeLists.txt =================================================================== --- flang/tools/tco/CMakeLists.txt +++ flang/tools/tco/CMakeLists.txt @@ -1,6 +1,15 @@ -get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +set(LLVM_LINK_COMPONENTS + AllTargetsAsmParsers + AllTargetsCodeGens + AllTargetsDescs + AllTargetsInfos +) +llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS}) -set(LIBS +add_flang_tool(tco tco.cpp) +llvm_update_compile_flags(tco) +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +target_link_libraries(tco PRIVATE FIROptimizer ${dialect_libs} MLIRIR @@ -16,7 +25,5 @@ MLIRStandardToLLVM MLIRSupport MLIRVectorToLLVM + ${llvm_libs} ) - -add_flang_tool(tco tco.cpp) -target_link_libraries(tco PRIVATE ${LIBS}) Index: flang/tools/tco/tco.cpp =================================================================== --- flang/tools/tco/tco.cpp +++ flang/tools/tco/tco.cpp @@ -12,7 +12,12 @@ //===----------------------------------------------------------------------===// #include "flang/Optimizer/Dialect/FIRDialect.h" +#include "flang/Optimizer/OptPasses.h" +#include "flang/Optimizer/Support/FIRContext.h" +#include "flang/Optimizer/Support/InternalNames.h" #include "flang/Optimizer/Support/KindMapping.h" +#include "mlir/Conversion/SCFToStandard/SCFToStandard.h" +#include "mlir/IR/AsmState.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/MLIRContext.h" #include "mlir/Parser.h" @@ -24,11 +29,13 @@ #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; +// list of program return codes static cl::opt inputFilename(cl::Positional, cl::desc(""), cl::init("-")); @@ -41,72 +48,107 @@ cl::desc("Parse and pretty-print the input"), cl::init(false)); +static cl::opt targetTriple("target", + cl::desc("specify a target triple"), + cl::init("native")); + static void printModuleBody(mlir::ModuleOp mod, raw_ostream &output) { for (auto &op : mod.getBody()->without_terminator()) output << op << '\n'; } // compile a .fir file -static int compileFIR() { +static mlir::LogicalResult +compileFIR(const mlir::PassPipelineCLParser &passPipeline) { // check that there is a file to load ErrorOr> fileOrErr = MemoryBuffer::getFileOrSTDIN(inputFilename); if (std::error_code EC = fileOrErr.getError()) { errs() << "Could not open file: " << EC.message() << '\n'; - return 1; + return mlir::failure(); } // load the file into a module SourceMgr sourceMgr; sourceMgr.AddNewSourceBuffer(std::move(*fileOrErr), SMLoc()); mlir::MLIRContext context; + fir::registerAndLoadDialects(context); auto owningRef = mlir::parseSourceFile(sourceMgr, &context); if (!owningRef) { errs() << "Error can't load file " << inputFilename << '\n'; - return 2; + return mlir::failure(); } if (mlir::failed(owningRef->verify())) { errs() << "Error verifying FIR module\n"; - return 4; + return mlir::failure(); } std::error_code ec; ToolOutputFile out(outputFilename, ec, sys::fs::OF_None); // run passes - mlir::PassManager pm{&context}; + llvm::Triple triple(fir::determineTargetTriple(targetTriple)); + fir::NameUniquer uniquer; + fir::KindMapping kindMap{&context}; + fir::setTargetTriple(*owningRef, triple); + fir::setNameUniquer(*owningRef, uniquer); + fir::setKindMapping(*owningRef, kindMap); + mlir::PassManager pm(&context, mlir::OpPassManager::Nesting::Implicit); + pm.enableVerifier(/*verifyPasses=*/true); mlir::applyPassManagerCLOptions(pm); if (emitFir) { // parse the input and pretty-print it back out // -emit-fir intentionally disables all the passes + } else if (passPipeline.hasAnyOccurrences()) { + passPipeline.addToPipeline(pm, [&](const Twine &msg) { + mlir::emitError(mlir::UnknownLoc::get(&context)) << msg; + return mlir::failure(); + }); } else { - // TODO: Actually add passes when added to FIR code base - // add all the passes - // the user can disable them individually + // simplify the IR + pm.addPass(mlir::createCanonicalizerPass()); + pm.addPass(mlir::createInlinerPass()); + pm.addPass(mlir::createCSEPass()); + + // convert control flow to CFG form + pm.addPass(mlir::createLowerToCFGPass()); + + pm.addPass(mlir::createCanonicalizerPass()); + + // pm.addPass(fir::createMemToRegPass()); + pm.addPass(fir::createFirCodeGenRewritePass()); + pm.addPass(fir::createFirTargetRewritePass()); + pm.addPass(fir::createFIRToLLVMPass(uniquer)); + pm.addPass(fir::createLLVMDialectToLLVMPass(out.os())); } // run the pass manager if (mlir::succeeded(pm.run(*owningRef))) { // passes ran successfully, so keep the output - if (emitFir) + if (emitFir || passPipeline.hasAnyOccurrences()) printModuleBody(*owningRef, out.os()); out.keep(); - return 0; + return mlir::success(); } // pass manager failed printModuleBody(*owningRef, errs()); errs() << "\n\nFAILED: " << inputFilename << '\n'; - return 8; + return mlir::failure(); } int main(int argc, char **argv) { fir::registerFIRPasses(); + fir::registerOptPasses(); + [[maybe_unused]] InitLLVM y(argc, argv); + InitializeAllTargets(); + mlir::registerAsmPrinterCLOptions(); + mlir::registerMLIRContextCLOptions(); mlir::registerPassManagerCLOptions(); mlir::PassPipelineCLParser passPipe("", "Compiler passes to run"); cl::ParseCommandLineOptions(argc, argv, "Tilikum Crossing Optimizer\n"); - return compileFIR(); + return mlir::failed(compileFIR(passPipe)); }