From 44bd69ee70b79ed8fe1cb11d728644b8dc99f8bf Mon Sep 17 00:00:00 2001 From: Viktoria Maximova Date: Thu, 28 Nov 2024 09:39:01 +0100 Subject: [PATCH] Translate OpIAddCarry and OpISubBorrow back to llvm intrinsics (#2877) Semantically we can translate `OpIAddCarry` and `OpISubBorrow` back into `@llvm.uadd.with.overflow` and `@llvm.usub.with.overflow` respectively. It require small transformation as there is a difference between return types in SPIR-V and LLVM IR - e.g., SPIR-V instructions return {i32, i32} struct, but LLVM intrinsics return {i32, i1}. --- lib/SPIRV/SPIRVReader.cpp | 34 ++++++- test/builtin_returns_struct.spvasm | 4 +- test/iaddcarry_builtin.ll | 48 ++++++++-- test/isubborrow_builtin.ll | 48 ++++++++-- test/llvm-intrinsics/uadd.with.overflow.ll | 104 +++++++++++++-------- test/llvm-intrinsics/usub.with.overflow.ll | 104 +++++++++++++-------- 6 files changed, 243 insertions(+), 99 deletions(-) diff --git a/lib/SPIRV/SPIRVReader.cpp b/lib/SPIRV/SPIRVReader.cpp index 6e0ae3a6e0..c0cdcd38ac 100644 --- a/lib/SPIRV/SPIRVReader.cpp +++ b/lib/SPIRV/SPIRVReader.cpp @@ -2714,13 +2714,37 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F, case OpSignBitSet: return mapValue(BV, transRelational(static_cast(BV), BB)); - case OpIAddCarry: { - auto *BC = static_cast(BV); - return mapValue(BV, transBuiltinFromInst("__spirv_IAddCarry", BC, BB)); - } + case OpIAddCarry: case OpISubBorrow: { + IRBuilder Builder(BB); auto *BC = static_cast(BV); - return mapValue(BV, transBuiltinFromInst("__spirv_ISubBorrow", BC, BB)); + Intrinsic::ID ID = OC == OpIAddCarry ? Intrinsic::uadd_with_overflow + : Intrinsic::usub_with_overflow; + auto *Inst = + Builder.CreateBinaryIntrinsic(ID, transValue(BC->getOperand(0), F, BB), + transValue(BC->getOperand(1), F, BB)); + + // Extract components of the result. + auto *Result = Builder.CreateExtractValue(Inst, 0); // iN result + auto *Carry = Builder.CreateExtractValue(Inst, 1); // i1 overflow + + // Convert {iN, i1} into {iN, iN} for SPIR-V compatibility. + Value *CarryInt; + if (Carry->getType()->isVectorTy()) { + CarryInt = Builder.CreateZExt( + Carry, VectorType::get( + cast(Result->getType())->getElementType(), + cast(Carry->getType())->getElementCount())); + } else { + CarryInt = Builder.CreateZExt(Carry, Result->getType()); + } + auto *ResultStruct = + Builder.CreateInsertValue(UndefValue::get(StructType::get( + Result->getType(), CarryInt->getType())), + Result, 0); + ResultStruct = Builder.CreateInsertValue(ResultStruct, CarryInt, 1); + + return mapValue(BV, ResultStruct); } case OpSMulExtended: { auto *BC = static_cast(BV); diff --git a/test/builtin_returns_struct.spvasm b/test/builtin_returns_struct.spvasm index 48e4973b73..fe16db24bb 100644 --- a/test/builtin_returns_struct.spvasm +++ b/test/builtin_returns_struct.spvasm @@ -34,7 +34,7 @@ OpBranch %20 %20 = OpLabel %e = OpPhi %uint %a %19 %math %21 - %16 = OpIAddCarry %_struct_7 %e %uint_1 + %16 = OpUMulExtended %_struct_7 %e %uint_1 %math = OpCompositeExtract %uint %16 0 %17 = OpCompositeExtract %uint %16 1 %ov = OpINotEqual %bool %17 %10 @@ -47,6 +47,6 @@ OpFunctionEnd ; CHECK: %[[#Var:]] = alloca %structtype, align 8 -; CHECK: call spir_func void @_Z17__spirv_IAddCarryii(ptr sret(%structtype) %[[#Var:]] +; CHECK: call spir_func void @_Z20__spirv_UMulExtendedii(ptr sret(%structtype) %[[#Var:]] ; CHECK: %[[#Load:]] = load %structtype, ptr %[[#Var]], align 4 ; CHECK-2: extractvalue %structtype %[[#Load:]] diff --git a/test/iaddcarry_builtin.ll b/test/iaddcarry_builtin.ll index d0fb1290be..a68bb1f425 100644 --- a/test/iaddcarry_builtin.ll +++ b/test/iaddcarry_builtin.ll @@ -57,7 +57,13 @@ define spir_func void @test_builtin_iaddcarrycc(i8 %a, i8 %b) { ; CHECK-SPIRV: OpFunctionEnd ; CHECK-LLVM: %0 = alloca [[i8struct]], align 8 -; CHECK-LLVM: call spir_func void @_Z17__spirv_IAddCarrycc(ptr sret([[i8struct]]) %0, i8 %a, i8 %b) +; CHECK-LLVM: %1 = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %a, i8 %b) +; CHECK-LLVM: %2 = extractvalue { i8, i1 } %1, 0 +; CHECK-LLVM: %3 = extractvalue { i8, i1 } %1, 1 +; CHECK-LLVM: %4 = zext i1 %3 to i8 +; CHECK-LLVM: %5 = insertvalue { i8, i8 } undef, i8 %2, 0 +; CHECK-LLVM: %6 = insertvalue { i8, i8 } %5, i8 %4, 1 +; CHECK-LLVM: store { i8, i8 } %6, ptr %0, align 1 ; CHECK-LLVM: ret void define spir_func void @test_builtin_iaddcarryss(i16 %a, i16 %b) { entry: @@ -75,7 +81,13 @@ define spir_func void @test_builtin_iaddcarryss(i16 %a, i16 %b) { ; CHECK-SPIRV: OpFunctionEnd ; CHECK-LLVM: %0 = alloca [[i16struct]], align 8 -; CHECK-LLVM: call spir_func void @_Z17__spirv_IAddCarryss(ptr sret([[i16struct]]) %0, i16 %a, i16 %b) +; CHECK-LLVM: %1 = call { i16, i1 } @llvm.uadd.with.overflow.i16(i16 %a, i16 %b) +; CHECK-LLVM: %2 = extractvalue { i16, i1 } %1, 0 +; CHECK-LLVM: %3 = extractvalue { i16, i1 } %1, 1 +; CHECK-LLVM: %4 = zext i1 %3 to i16 +; CHECK-LLVM: %5 = insertvalue { i16, i16 } undef, i16 %2, 0 +; CHECK-LLVM: %6 = insertvalue { i16, i16 } %5, i16 %4, 1 +; CHECK-LLVM: store { i16, i16 } %6, ptr %0, align 2 ; CHECK-LLVM: ret void define spir_func void @test_builtin_iaddcarryii(i32 %a, i32 %b) { entry: @@ -93,7 +105,13 @@ define spir_func void @test_builtin_iaddcarryii(i32 %a, i32 %b) { ; CHECK-SPIRV: OpFunctionEnd ; CHECK-LLVM: %0 = alloca [[i32struct]], align 8 -; CHECK-LLVM: call spir_func void @_Z17__spirv_IAddCarryii(ptr sret([[i32struct]]) %0, i32 %a, i32 %b) +; CHECK-LLVM: %1 = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %a, i32 %b) +; CHECK-LLVM: %2 = extractvalue { i32, i1 } %1, 0 +; CHECK-LLVM: %3 = extractvalue { i32, i1 } %1, 1 +; CHECK-LLVM: %4 = zext i1 %3 to i32 +; CHECK-LLVM: %5 = insertvalue { i32, i32 } undef, i32 %2, 0 +; CHECK-LLVM: %6 = insertvalue { i32, i32 } %5, i32 %4, 1 +; CHECK-LLVM: store { i32, i32 } %6, ptr %0, align 4 ; CHECK-LLVM: ret void define spir_func void @test_builtin_iaddcarryll(i64 %a, i64 %b) { entry: @@ -111,7 +129,13 @@ define spir_func void @test_builtin_iaddcarryll(i64 %a, i64 %b) { ; CHECK-SPIRV: OpFunctionEnd ; CHECK-LLVM: %0 = alloca [[i64struct]] -; CHECK-LLVM: call spir_func void @_Z17__spirv_IAddCarryll(ptr sret([[i64struct]]) %0, i64 %a, i64 %b) +; CHECK-LLVM: %1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %a, i64 %b) +; CHECK-LLVM: %2 = extractvalue { i64, i1 } %1, 0 +; CHECK-LLVM: %3 = extractvalue { i64, i1 } %1, 1 +; CHECK-LLVM: %4 = zext i1 %3 to i64 +; CHECK-LLVM: %5 = insertvalue { i64, i64 } undef, i64 %2, 0 +; CHECK-LLVM: %6 = insertvalue { i64, i64 } %5, i64 %4, 1 +; CHECK-LLVM: store { i64, i64 } %6, ptr %0, align 8 ; CHECK-LLVM: ret void define spir_func void @test_builtin_iaddcarryDv4_xS_(<4 x i32> %a, <4 x i32> %b) { entry: @@ -129,7 +153,13 @@ define spir_func void @test_builtin_iaddcarryDv4_xS_(<4 x i32> %a, <4 x i32> %b) ; CHECK-SPIRV: OpFunctionEnd ; CHECK-LLVM: %0 = alloca [[vecstruct]] -; CHECK-LLVM: call spir_func void @_Z17__spirv_IAddCarryDv4_iS_(ptr sret([[vecstruct]]) %0, <4 x i32> %a, <4 x i32> %b) +; CHECK-LLVM: %1 = call { <4 x i32>, <4 x i1> } @llvm.uadd.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b) +; CHECK-LLVM: %2 = extractvalue { <4 x i32>, <4 x i1> } %1, 0 +; CHECK-LLVM: %3 = extractvalue { <4 x i32>, <4 x i1> } %1, 1 +; CHECK-LLVM: %4 = zext <4 x i1> %3 to <4 x i32> +; CHECK-LLVM: %5 = insertvalue { <4 x i32>, <4 x i32> } undef, <4 x i32> %2, 0 +; CHECK-LLVM: %6 = insertvalue { <4 x i32>, <4 x i32> } %5, <4 x i32> %4, 1 +; CHECK-LLVM: store { <4 x i32>, <4 x i32> } %6, ptr %0, align 16 ; CHECK-LLVM: ret void %struct.anon = type { i32, i32 } @@ -151,7 +181,13 @@ define spir_func void @test_builtin_iaddcarry_anon(i32 %a, i32 %b) { ; CHECK-LLVM: %0 = alloca [[struct_anon]], align 8 ; CHECK-LLVM: %1 = addrspacecast ptr %0 to ptr addrspace(4) -; CHECK-LLVM: call spir_func void @_Z17__spirv_IAddCarryii.1(ptr addrspace(4) sret([[struct_anon]]) %1, i32 %a, i32 %b) +; CHECK-LLVM: %2 = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %a, i32 %b) +; CHECK-LLVM: %3 = extractvalue { i32, i1 } %2, 0 +; CHECK-LLVM: %4 = extractvalue { i32, i1 } %2, 1 +; CHECK-LLVM: %5 = zext i1 %4 to i32 +; CHECK-LLVM: %6 = insertvalue { i32, i32 } undef, i32 %3, 0 +; CHECK-LLVM: %7 = insertvalue { i32, i32 } %6, i32 %5, 1 +; CHECK-LLVM: store { i32, i32 } %7, ptr addrspace(4) %1, align 4 ; CHECK-LLVM: ret void declare void @_Z17__spirv_IAddCarryIiiE4anonIT_T0_ES1_S2_(ptr addrspace(4) sret(%struct.anon) align 4, i32, i32) diff --git a/test/isubborrow_builtin.ll b/test/isubborrow_builtin.ll index a9c143d065..dfcc37883b 100644 --- a/test/isubborrow_builtin.ll +++ b/test/isubborrow_builtin.ll @@ -58,7 +58,13 @@ define spir_func void @test_builtin_isubborrowcc(i8 %a, i8 %b) { ; CHECK-SPIRV: OpFunctionEnd ; CHECK-LLVM: %0 = alloca [[i8struct]], align 8 -; CHECK-LLVM: call spir_func void @_Z18__spirv_ISubBorrowcc(ptr sret([[i8struct]]) %0, i8 %a, i8 %b) +; CHECK-LLVM: %1 = call { i8, i1 } @llvm.usub.with.overflow.i8(i8 %a, i8 %b) +; CHECK-LLVM: %2 = extractvalue { i8, i1 } %1, 0 +; CHECK-LLVM: %3 = extractvalue { i8, i1 } %1, 1 +; CHECK-LLVM: %4 = zext i1 %3 to i8 +; CHECK-LLVM: %5 = insertvalue { i8, i8 } undef, i8 %2, 0 +; CHECK-LLVM: %6 = insertvalue { i8, i8 } %5, i8 %4, 1 +; CHECK-LLVM: store { i8, i8 } %6, ptr %0, align 1 ; CHECK-LLVM: ret void define spir_func void @test_builtin_isubborrowss(i16 %a, i16 %b) { entry: @@ -76,7 +82,13 @@ define spir_func void @test_builtin_isubborrowss(i16 %a, i16 %b) { ; CHECK-SPIRV: OpFunctionEnd ; CHECK-LLVM: %0 = alloca [[i16struct]], align 8 -; CHECK-LLVM: call spir_func void @_Z18__spirv_ISubBorrowss(ptr sret([[i16struct]]) %0, i16 %a, i16 %b) +; CHECK-LLVM: %1 = call { i16, i1 } @llvm.usub.with.overflow.i16(i16 %a, i16 %b) +; CHECK-LLVM: %2 = extractvalue { i16, i1 } %1, 0 +; CHECK-LLVM: %3 = extractvalue { i16, i1 } %1, 1 +; CHECK-LLVM: %4 = zext i1 %3 to i16 +; CHECK-LLVM: %5 = insertvalue { i16, i16 } undef, i16 %2, 0 +; CHECK-LLVM: %6 = insertvalue { i16, i16 } %5, i16 %4, 1 +; CHECK-LLVM: store { i16, i16 } %6, ptr %0, align 2 ; CHECK-LLVM: ret void define spir_func void @test_builtin_isubborrowii(i32 %a, i32 %b) { entry: @@ -94,7 +106,13 @@ define spir_func void @test_builtin_isubborrowii(i32 %a, i32 %b) { ; CHECK-SPIRV: OpFunctionEnd ; CHECK-LLVM: %0 = alloca [[i32struct]], align 8 -; CHECK-LLVM: call spir_func void @_Z18__spirv_ISubBorrowii(ptr sret([[i32struct]]) %0, i32 %a, i32 %b) +; CHECK-LLVM: %1 = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 %a, i32 %b) +; CHECK-LLVM: %2 = extractvalue { i32, i1 } %1, 0 +; CHECK-LLVM: %3 = extractvalue { i32, i1 } %1, 1 +; CHECK-LLVM: %4 = zext i1 %3 to i32 +; CHECK-LLVM: %5 = insertvalue { i32, i32 } undef, i32 %2, 0 +; CHECK-LLVM: %6 = insertvalue { i32, i32 } %5, i32 %4, 1 +; CHECK-LLVM: store { i32, i32 } %6, ptr %0, align 4 ; CHECK-LLVM: ret void define spir_func void @test_builtin_isubborrowll(i64 %a, i64 %b) { entry: @@ -112,7 +130,13 @@ define spir_func void @test_builtin_isubborrowll(i64 %a, i64 %b) { ; CHECK-SPIRV: OpFunctionEnd ; CHECK-LLVM: %0 = alloca [[i64struct]] -; CHECK-LLVM: call spir_func void @_Z18__spirv_ISubBorrowll(ptr sret([[i64struct]]) %0, i64 %a, i64 %b) +; CHECK-LLVM: %1 = call { i64, i1 } @llvm.usub.with.overflow.i64(i64 %a, i64 %b) +; CHECK-LLVM: %2 = extractvalue { i64, i1 } %1, 0 +; CHECK-LLVM: %3 = extractvalue { i64, i1 } %1, 1 +; CHECK-LLVM: %4 = zext i1 %3 to i64 +; CHECK-LLVM: %5 = insertvalue { i64, i64 } undef, i64 %2, 0 +; CHECK-LLVM: %6 = insertvalue { i64, i64 } %5, i64 %4, 1 +; CHECK-LLVM: store { i64, i64 } %6, ptr %0, align 8 ; CHECK-LLVM: ret void define spir_func void @test_builtin_isubborrowDv4_xS_(<4 x i32> %a, <4 x i32> %b) { entry: @@ -130,7 +154,13 @@ define spir_func void @test_builtin_isubborrowDv4_xS_(<4 x i32> %a, <4 x i32> %b ; CHECK-SPIRV: OpFunctionEnd ; CHECK-LLVM: %0 = alloca [[vecstruct]] -; CHECK-LLVM: call spir_func void @_Z18__spirv_ISubBorrowDv4_iS_(ptr sret([[vecstruct]]) %0, <4 x i32> %a, <4 x i32> %b) +; CHECK-LLVM: %1 = call { <4 x i32>, <4 x i1> } @llvm.usub.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b) +; CHECK-LLVM: %2 = extractvalue { <4 x i32>, <4 x i1> } %1, 0 +; CHECK-LLVM: %3 = extractvalue { <4 x i32>, <4 x i1> } %1, 1 +; CHECK-LLVM: %4 = zext <4 x i1> %3 to <4 x i32> +; CHECK-LLVM: %5 = insertvalue { <4 x i32>, <4 x i32> } undef, <4 x i32> %2, 0 +; CHECK-LLVM: %6 = insertvalue { <4 x i32>, <4 x i32> } %5, <4 x i32> %4, 1 +; CHECK-LLVM: store { <4 x i32>, <4 x i32> } %6, ptr %0, align 16 ; CHECK-LLVM: ret void @@ -151,7 +181,13 @@ define spir_func void @test_builtin_isubborrow_anon(i32 %a, i32 %b) { ; CHECK-LLVM: %0 = alloca [[struct_anon]], align 8 ; CHECK-LLVM: %1 = addrspacecast ptr %0 to ptr addrspace(4) -; CHECK-LLVM: call spir_func void @_Z18__spirv_ISubBorrowii.1(ptr addrspace(4) sret([[struct_anon]]) %1, i32 %a, i32 %b) +; CHECK-LLVM: %2 = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 %a, i32 %b) +; CHECK-LLVM: %3 = extractvalue { i32, i1 } %2, 0 +; CHECK-LLVM: %4 = extractvalue { i32, i1 } %2, 1 +; CHECK-LLVM: %5 = zext i1 %4 to i32 +; CHECK-LLVM: %6 = insertvalue { i32, i32 } undef, i32 %3, 0 +; CHECK-LLVM: %7 = insertvalue { i32, i32 } %6, i32 %5, 1 +; CHECK-LLVM: store { i32, i32 } %7, ptr addrspace(4) %1, align 4 ; CHECK-LLVM: ret void declare void @_Z18__spirv_ISubBorrowIiiE4anonIT_T0_ES1_S2_(ptr addrspace(4) sret(%struct.anon) align 4, i32, i32) diff --git a/test/llvm-intrinsics/uadd.with.overflow.ll b/test/llvm-intrinsics/uadd.with.overflow.ll index 33fec83f09..8ed0385367 100644 --- a/test/llvm-intrinsics/uadd.with.overflow.ll +++ b/test/llvm-intrinsics/uadd.with.overflow.ll @@ -73,16 +73,22 @@ entry: ; CHECK-SPIRV: OpReturnValue [[var_23]] ; CHECK-LLVM: %0 = alloca [[structtype]], align 8 -; CHECK-LLVM: call spir_func void @_Z17__spirv_IAddCarryss(ptr sret([[structtype]]) %0, i16 %a, i16 %b) -; CHECK-LLVM: %1 = load [[structtype]], ptr %0, align 2 -; CHECK-LLVM: %2 = extractvalue [[structtype]] %1, 0 -; CHECK-LLVM: %3 = extractvalue [[structtype]] %1, 1 -; CHECK-LLVM: %4 = icmp ne i16 %3, 0 -; CHECK-LLVM: %5 = insertvalue [[structtype_0]] undef, i16 %2, 0 -; CHECK-LLVM: %6 = insertvalue [[structtype_0]] %5, i1 %4, 1 -; CHECK-LLVM: %7 = extractvalue [[structtype_0]] %6, 0 -; CHECK-LLVM: %8 = extractvalue [[structtype_0]] %6, 1 -; CHECK-LLVM: ret i1 %8 +; CHECK-LLVM: %1 = call { i16, i1 } @llvm.uadd.with.overflow.i16(i16 %a, i16 %b) +; CHECK-LLVM: %2 = extractvalue { i16, i1 } %1, 0 +; CHECK-LLVM: %3 = extractvalue { i16, i1 } %1, 1 +; CHECK-LLVM: %4 = zext i1 %3 to i16 +; CHECK-LLVM: %5 = insertvalue { i16, i16 } undef, i16 %2, 0 +; CHECK-LLVM: %6 = insertvalue { i16, i16 } %5, i16 %4, 1 +; CHECK-LLVM: store { i16, i16 } %6, ptr %0, align 2 +; CHECK-LLVM: %7 = load %structtype, ptr %0, align 2 +; CHECK-LLVM: %8 = extractvalue [[structtype]] %7, 0 +; CHECK-LLVM: %9 = extractvalue [[structtype]] %7, 1 +; CHECK-LLVM: %10 = icmp ne i16 %9, 0 +; CHECK-LLVM: %11 = insertvalue [[structtype_0]] undef, i16 %8, 0 +; CHECK-LLVM: %12 = insertvalue [[structtype_0]] %11, i1 %10, 1 +; CHECK-LLVM: %13 = extractvalue [[structtype_0]] %12, 0 +; CHECK-LLVM: %14 = extractvalue [[structtype_0]] %12, 1 +; CHECK-LLVM: ret i1 %14 define spir_func i1 @test_uadd_with_overflow_i32(i32 %a, i32 %b) { entry: %res = call {i32, i1} @llvm.uadd.with.overflow.i32(i32 %a, i32 %b) @@ -109,16 +115,22 @@ entry: ; CHECK-LLVM: %0 = alloca [[structtype_1]], align 8 -; CHECK-LLVM: call spir_func void @_Z17__spirv_IAddCarryii(ptr sret([[structtype_1]]) %0, i32 %a, i32 %b) -; CHECK-LLVM: %1 = load [[structtype_1]], ptr %0, align 4 -; CHECK-LLVM: %2 = extractvalue [[structtype_1]] %1, 0 -; CHECK-LLVM: %3 = extractvalue [[structtype_1]] %1, 1 -; CHECK-LLVM: %4 = icmp ne i32 %3, 0 -; CHECK-LLVM: %5 = insertvalue [[structtype_2]] undef, i32 %2, 0 -; CHECK-LLVM: %6 = insertvalue [[structtype_2]] %5, i1 %4, 1 -; CHECK-LLVM: %7 = extractvalue [[structtype_2]] %6, 0 -; CHECK-LLVM: %8 = extractvalue [[structtype_2]] %6, 1 -; CHECK-LLVM: ret i1 %8 +; CHECK-LLVM: %1 = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 %a, i32 %b) +; CHECK-LLVM: %2 = extractvalue { i32, i1 } %1, 0 +; CHECK-LLVM: %3 = extractvalue { i32, i1 } %1, 1 +; CHECK-LLVM: %4 = zext i1 %3 to i32 +; CHECK-LLVM: %5 = insertvalue { i32, i32 } undef, i32 %2, 0 +; CHECK-LLVM: %6 = insertvalue { i32, i32 } %5, i32 %4, 1 +; CHECK-LLVM: store { i32, i32 } %6, ptr %0, align 4 +; CHECK-LLVM: %7 = load [[structtype_1]], ptr %0, align 4 +; CHECK-LLVM: %8 = extractvalue [[structtype_1]] %7, 0 +; CHECK-LLVM: %9 = extractvalue [[structtype_1]] %7, 1 +; CHECK-LLVM: %10 = icmp ne i32 %9, 0 +; CHECK-LLVM: %11 = insertvalue [[structtype_2]] undef, i32 %8, 0 +; CHECK-LLVM: %12 = insertvalue [[structtype_2]] %11, i1 %10, 1 +; CHECK-LLVM: %13 = extractvalue [[structtype_2]] %12, 0 +; CHECK-LLVM: %14 = extractvalue [[structtype_2]] %12, 1 +; CHECK-LLVM: ret i1 %14 define spir_func i1 @test_uadd_with_overflow_i64(i64 %a, i64 %b) { entry: %res = call {i64, i1} @llvm.uadd.with.overflow.i64(i64 %a, i64 %b) @@ -144,16 +156,22 @@ entry: ; CHECK-SPIRV: OpReturnValue [[var_65]] ; CHECK-LLVM: %0 = alloca [[structtype_3]], align 8 -; CHECK-LLVM: call spir_func void @_Z17__spirv_IAddCarryll(ptr sret([[structtype_3]]) %0, i64 %a, i64 %b) -; CHECK-LLVM: %1 = load [[structtype_3]], ptr %0, align 4 -; CHECK-LLVM: %2 = extractvalue [[structtype_3]] %1, 0 -; CHECK-LLVM: %3 = extractvalue [[structtype_3]] %1, 1 -; CHECK-LLVM: %4 = icmp ne i64 %3, 0 -; CHECK-LLVM: %5 = insertvalue [[structtype_4]] undef, i64 %2, 0 -; CHECK-LLVM: %6 = insertvalue [[structtype_4]] %5, i1 %4, 1 -; CHECK-LLVM: %7 = extractvalue [[structtype_4]] %6, 0 -; CHECK-LLVM: %8 = extractvalue [[structtype_4]] %6, 1 -; CHECK-LLVM: ret i1 %8 +; CHECK-LLVM: %1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %a, i64 %b) +; CHECK-LLVM: %2 = extractvalue { i64, i1 } %1, 0 +; CHECK-LLVM: %3 = extractvalue { i64, i1 } %1, 1 +; CHECK-LLVM: %4 = zext i1 %3 to i64 +; CHECK-LLVM: %5 = insertvalue { i64, i64 } undef, i64 %2, 0 +; CHECK-LLVM: %6 = insertvalue { i64, i64 } %5, i64 %4, 1 +; CHECK-LLVM: store { i64, i64 } %6, ptr %0, align 8 +; CHECK-LLVM: %7 = load [[structtype_3]], ptr %0, align 4 +; CHECK-LLVM: %8 = extractvalue [[structtype_3]] %7, 0 +; CHECK-LLVM: %9 = extractvalue [[structtype_3]] %7, 1 +; CHECK-LLVM: %10 = icmp ne i64 %9, 0 +; CHECK-LLVM: %11 = insertvalue [[structtype_4]] undef, i64 %8, 0 +; CHECK-LLVM: %12 = insertvalue [[structtype_4]] %11, i1 %10, 1 +; CHECK-LLVM: %13 = extractvalue [[structtype_4]] %12, 0 +; CHECK-LLVM: %14 = extractvalue [[structtype_4]] %12, 1 +; CHECK-LLVM: ret i1 %14 define spir_func <4 x i1> @test_uadd_with_overflow_v4i32(<4 x i32> %a, <4 x i32> %b) { entry: %res = call {<4 x i32>, <4 x i1>} @llvm.uadd.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b) @@ -179,16 +197,22 @@ entry: ; CHECK-SPIRV: OpReturnValue [[var_87]] ; CHECK-LLVM: %0 = alloca [[structtype_5]], align 16 -; CHECK-LLVM: call spir_func void @_Z17__spirv_IAddCarryDv4_iS_(ptr sret([[structtype_5]]) %0, <4 x i32> %a, <4 x i32> %b) -; CHECK-LLVM: %1 = load [[structtype_5]], ptr %0, align 16 -; CHECK-LLVM: %2 = extractvalue [[structtype_5]] %1, 0 -; CHECK-LLVM: %3 = extractvalue [[structtype_5]] %1, 1 -; CHECK-LLVM: %4 = icmp ne <4 x i32> %3, zeroinitializer -; CHECK-LLVM: %5 = insertvalue [[structtype_6]] undef, <4 x i32> %2, 0 -; CHECK-LLVM: %6 = insertvalue [[structtype_6]] %5, <4 x i1> %4, 1 -; CHECK-LLVM: %7 = extractvalue [[structtype_6]] %6, 0 -; CHECK-LLVM: %8 = extractvalue [[structtype_6]] %6, 1 -; CHECK-LLVM: ret <4 x i1> %8 +; CHECK-LLVM: %1 = call { <4 x i32>, <4 x i1> } @llvm.uadd.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b) +; CHECK-LLVM: %2 = extractvalue { <4 x i32>, <4 x i1> } %1, 0 +; CHECK-LLVM: %3 = extractvalue { <4 x i32>, <4 x i1> } %1, 1 +; CHECK-LLVM: %4 = zext <4 x i1> %3 to <4 x i32> +; CHECK-LLVM: %5 = insertvalue { <4 x i32>, <4 x i32> } undef, <4 x i32> %2, 0 +; CHECK-LLVM: %6 = insertvalue { <4 x i32>, <4 x i32> } %5, <4 x i32> %4, 1 +; CHECK-LLVM: store { <4 x i32>, <4 x i32> } %6, ptr %0, align 16 +; CHECK-LLVM: %7 = load [[structtype_5]], ptr %0, align 16 +; CHECK-LLVM: %8 = extractvalue [[structtype_5]] %7, 0 +; CHECK-LLVM: %9 = extractvalue [[structtype_5]] %7, 1 +; CHECK-LLVM: %10 = icmp ne <4 x i32> %9, zeroinitializer +; CHECK-LLVM: %11 = insertvalue [[structtype_6]] undef, <4 x i32> %8, 0 +; CHECK-LLVM: %12 = insertvalue [[structtype_6]] %11, <4 x i1> %10, 1 +; CHECK-LLVM: %13 = extractvalue [[structtype_6]] %12, 0 +; CHECK-LLVM: %14 = extractvalue [[structtype_6]] %12, 1 +; CHECK-LLVM: ret <4 x i1> %14 declare {i16, i1} @llvm.uadd.with.overflow.i16(i16 %a, i16 %b) declare {i32, i1} @llvm.uadd.with.overflow.i32(i32 %a, i32 %b) declare {i64, i1} @llvm.uadd.with.overflow.i64(i64 %a, i64 %b) diff --git a/test/llvm-intrinsics/usub.with.overflow.ll b/test/llvm-intrinsics/usub.with.overflow.ll index 5225345adb..8505c395f9 100644 --- a/test/llvm-intrinsics/usub.with.overflow.ll +++ b/test/llvm-intrinsics/usub.with.overflow.ll @@ -73,16 +73,22 @@ entry: ; CHECK-SPIRV: OpReturnValue [[var_23]] ; CHECK-LLVM: %0 = alloca [[structtype]], align 8 -; CHECK-LLVM: call spir_func void @_Z18__spirv_ISubBorrowss(ptr sret([[structtype]]) %0, i16 %a, i16 %b) -; CHECK-LLVM: %1 = load [[structtype]], ptr %0, align 2 -; CHECK-LLVM: %2 = extractvalue [[structtype]] %1, 0 -; CHECK-LLVM: %3 = extractvalue [[structtype]] %1, 1 -; CHECK-LLVM: %4 = icmp ne i16 %3, 0 -; CHECK-LLVM: %5 = insertvalue [[structtype_0]] undef, i16 %2, 0 -; CHECK-LLVM: %6 = insertvalue [[structtype_0]] %5, i1 %4, 1 -; CHECK-LLVM: %7 = extractvalue [[structtype_0]] %6, 0 -; CHECK-LLVM: %8 = extractvalue [[structtype_0]] %6, 1 -; CHECK-LLVM: ret i1 %8 +; CHECK-LLVM: %1 = call { i16, i1 } @llvm.usub.with.overflow.i16(i16 %a, i16 %b) +; CHECK-LLVM: %2 = extractvalue { i16, i1 } %1, 0 +; CHECK-LLVM: %3 = extractvalue { i16, i1 } %1, 1 +; CHECK-LLVM: %4 = zext i1 %3 to i16 +; CHECK-LLVM: %5 = insertvalue { i16, i16 } undef, i16 %2, 0 +; CHECK-LLVM: %6 = insertvalue { i16, i16 } %5, i16 %4, 1 +; CHECK-LLVM: store { i16, i16 } %6, ptr %0, align 2 +; CHECK-LLVM: %7 = load %structtype, ptr %0, align 2 +; CHECK-LLVM: %8 = extractvalue [[structtype]] %7, 0 +; CHECK-LLVM: %9 = extractvalue [[structtype]] %7, 1 +; CHECK-LLVM: %10 = icmp ne i16 %9, 0 +; CHECK-LLVM: %11 = insertvalue [[structtype_0]] undef, i16 %8, 0 +; CHECK-LLVM: %12 = insertvalue [[structtype_0]] %11, i1 %10, 1 +; CHECK-LLVM: %13 = extractvalue [[structtype_0]] %12, 0 +; CHECK-LLVM: %14 = extractvalue [[structtype_0]] %12, 1 +; CHECK-LLVM: ret i1 %14 define spir_func i1 @test_usub_with_overflow_i32(i32 %a, i32 %b) { entry: %res = call {i32, i1} @llvm.usub.with.overflow.i32(i32 %a, i32 %b) @@ -109,16 +115,22 @@ entry: ; CHECK-LLVM: %0 = alloca [[structtype_1]], align 8 -; CHECK-LLVM: call spir_func void @_Z18__spirv_ISubBorrowii(ptr sret([[structtype_1]]) %0, i32 %a, i32 %b) -; CHECK-LLVM: %1 = load [[structtype_1]], ptr %0, align 4 -; CHECK-LLVM: %2 = extractvalue [[structtype_1]] %1, 0 -; CHECK-LLVM: %3 = extractvalue [[structtype_1]] %1, 1 -; CHECK-LLVM: %4 = icmp ne i32 %3, 0 -; CHECK-LLVM: %5 = insertvalue [[structtype_2]] undef, i32 %2, 0 -; CHECK-LLVM: %6 = insertvalue [[structtype_2]] %5, i1 %4, 1 -; CHECK-LLVM: %7 = extractvalue [[structtype_2]] %6, 0 -; CHECK-LLVM: %8 = extractvalue [[structtype_2]] %6, 1 -; CHECK-LLVM: ret i1 %8 +; CHECK-LLVM: %1 = call { i32, i1 } @llvm.usub.with.overflow.i32(i32 %a, i32 %b) +; CHECK-LLVM: %2 = extractvalue { i32, i1 } %1, 0 +; CHECK-LLVM: %3 = extractvalue { i32, i1 } %1, 1 +; CHECK-LLVM: %4 = zext i1 %3 to i32 +; CHECK-LLVM: %5 = insertvalue { i32, i32 } undef, i32 %2, 0 +; CHECK-LLVM: %6 = insertvalue { i32, i32 } %5, i32 %4, 1 +; CHECK-LLVM: store { i32, i32 } %6, ptr %0, align 4 +; CHECK-LLVM: %7 = load [[structtype_1]], ptr %0, align 4 +; CHECK-LLVM: %8 = extractvalue [[structtype_1]] %7, 0 +; CHECK-LLVM: %9 = extractvalue [[structtype_1]] %7, 1 +; CHECK-LLVM: %10 = icmp ne i32 %9, 0 +; CHECK-LLVM: %11 = insertvalue [[structtype_2]] undef, i32 %8, 0 +; CHECK-LLVM: %12 = insertvalue [[structtype_2]] %11, i1 %10, 1 +; CHECK-LLVM: %13 = extractvalue [[structtype_2]] %12, 0 +; CHECK-LLVM: %14 = extractvalue [[structtype_2]] %12, 1 +; CHECK-LLVM: ret i1 %14 define spir_func i1 @test_usub_with_overflow_i64(i64 %a, i64 %b) { entry: %res = call {i64, i1} @llvm.usub.with.overflow.i64(i64 %a, i64 %b) @@ -144,16 +156,22 @@ entry: ; CHECK-SPIRV: OpReturnValue [[var_65]] ; CHECK-LLVM: %0 = alloca [[structtype_3]], align 8 -; CHECK-LLVM: call spir_func void @_Z18__spirv_ISubBorrowll(ptr sret([[structtype_3]]) %0, i64 %a, i64 %b) -; CHECK-LLVM: %1 = load [[structtype_3]], ptr %0, align 4 -; CHECK-LLVM: %2 = extractvalue [[structtype_3]] %1, 0 -; CHECK-LLVM: %3 = extractvalue [[structtype_3]] %1, 1 -; CHECK-LLVM: %4 = icmp ne i64 %3, 0 -; CHECK-LLVM: %5 = insertvalue [[structtype_4]] undef, i64 %2, 0 -; CHECK-LLVM: %6 = insertvalue [[structtype_4]] %5, i1 %4, 1 -; CHECK-LLVM: %7 = extractvalue [[structtype_4]] %6, 0 -; CHECK-LLVM: %8 = extractvalue [[structtype_4]] %6, 1 -; CHECK-LLVM: ret i1 %8 +; CHECK-LLVM: %1 = call { i64, i1 } @llvm.usub.with.overflow.i64(i64 %a, i64 %b) +; CHECK-LLVM: %2 = extractvalue { i64, i1 } %1, 0 +; CHECK-LLVM: %3 = extractvalue { i64, i1 } %1, 1 +; CHECK-LLVM: %4 = zext i1 %3 to i64 +; CHECK-LLVM: %5 = insertvalue { i64, i64 } undef, i64 %2, 0 +; CHECK-LLVM: %6 = insertvalue { i64, i64 } %5, i64 %4, 1 +; CHECK-LLVM: store { i64, i64 } %6, ptr %0, align 8 +; CHECK-LLVM: %7 = load [[structtype_3]], ptr %0, align 4 +; CHECK-LLVM: %8 = extractvalue [[structtype_3]] %7, 0 +; CHECK-LLVM: %9 = extractvalue [[structtype_3]] %7, 1 +; CHECK-LLVM: %10 = icmp ne i64 %9, 0 +; CHECK-LLVM: %11 = insertvalue [[structtype_4]] undef, i64 %8, 0 +; CHECK-LLVM: %12 = insertvalue [[structtype_4]] %11, i1 %10, 1 +; CHECK-LLVM: %13 = extractvalue [[structtype_4]] %12, 0 +; CHECK-LLVM: %14 = extractvalue [[structtype_4]] %12, 1 +; CHECK-LLVM: ret i1 %14 define spir_func <4 x i1> @test_usub_with_overflow_v4i32(<4 x i32> %a, <4 x i32> %b) { entry: %res = call {<4 x i32>, <4 x i1>} @llvm.usub.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b) @@ -179,16 +197,22 @@ entry: ; CHECK-SPIRV: OpReturnValue [[var_87]] ; CHECK-LLVM: %0 = alloca [[structtype_5]], align 16 -; CHECK-LLVM: call spir_func void @_Z18__spirv_ISubBorrowDv4_iS_(ptr sret([[structtype_5]]) %0, <4 x i32> %a, <4 x i32> %b) -; CHECK-LLVM: %1 = load [[structtype_5]], ptr %0, align 16 -; CHECK-LLVM: %2 = extractvalue [[structtype_5]] %1, 0 -; CHECK-LLVM: %3 = extractvalue [[structtype_5]] %1, 1 -; CHECK-LLVM: %4 = icmp ne <4 x i32> %3, zeroinitializer -; CHECK-LLVM: %5 = insertvalue [[structtype_6]] undef, <4 x i32> %2, 0 -; CHECK-LLVM: %6 = insertvalue [[structtype_6]] %5, <4 x i1> %4, 1 -; CHECK-LLVM: %7 = extractvalue [[structtype_6]] %6, 0 -; CHECK-LLVM: %8 = extractvalue [[structtype_6]] %6, 1 -; CHECK-LLVM: ret <4 x i1> %8 +; CHECK-LLVM: %1 = call { <4 x i32>, <4 x i1> } @llvm.usub.with.overflow.v4i32(<4 x i32> %a, <4 x i32> %b) +; CHECK-LLVM: %2 = extractvalue { <4 x i32>, <4 x i1> } %1, 0 +; CHECK-LLVM: %3 = extractvalue { <4 x i32>, <4 x i1> } %1, 1 +; CHECK-LLVM: %4 = zext <4 x i1> %3 to <4 x i32> +; CHECK-LLVM: %5 = insertvalue { <4 x i32>, <4 x i32> } undef, <4 x i32> %2, 0 +; CHECK-LLVM: %6 = insertvalue { <4 x i32>, <4 x i32> } %5, <4 x i32> %4, 1 +; CHECK-LLVM: store { <4 x i32>, <4 x i32> } %6, ptr %0, align 16 +; CHECK-LLVM: %7 = load [[structtype_5]], ptr %0, align 16 +; CHECK-LLVM: %8 = extractvalue [[structtype_5]] %7, 0 +; CHECK-LLVM: %9 = extractvalue [[structtype_5]] %7, 1 +; CHECK-LLVM: %10 = icmp ne <4 x i32> %9, zeroinitializer +; CHECK-LLVM: %11 = insertvalue [[structtype_6]] undef, <4 x i32> %8, 0 +; CHECK-LLVM: %12 = insertvalue [[structtype_6]] %11, <4 x i1> %10, 1 +; CHECK-LLVM: %13 = extractvalue [[structtype_6]] %12, 0 +; CHECK-LLVM: %14 = extractvalue [[structtype_6]] %12, 1 +; CHECK-LLVM: ret <4 x i1> %14 declare {i16, i1} @llvm.usub.with.overflow.i16(i16 %a, i16 %b) declare {i32, i1} @llvm.usub.with.overflow.i32(i32 %a, i32 %b) declare {i64, i1} @llvm.usub.with.overflow.i64(i64 %a, i64 %b)