diff --git a/llvm/test/Transforms/InstCombine/gep-merge-constant-indices.ll b/llvm/test/Transforms/InstCombine/gep-merge-constant-indices.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/gep-merge-constant-indices.ll @@ -0,0 +1,380 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -opaque-pointers -S | FileCheck %s + +; Test simplifying nested GEP instructions containing constant indices at the +; first and last GEP. + +%struct.A = type { [123 x i8], i32 } +%struct.B = type { i8, [3 x i16], %struct.A, float } +%struct.C = type { i8, i32, i32 } + +; result = (p + 3) + a +define i32* @basic(i32* %p, i64 %a) { +; CHECK-LABEL: @basic( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 2 +; CHECK-NEXT: ret ptr [[TMP3]] +; + %1 = getelementptr inbounds i32, i32* %p, i64 1 + %2 = getelementptr inbounds i32, i32* %1, i64 %a + %3 = getelementptr inbounds i32, i32* %2, i64 2 + ret i32* %3 +} + +; result = (struct.C*) p + 3 +define %struct.C* @offsetDivisible(i64* %p) { +; CHECK-LABEL: @offsetDivisible( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 3 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[TMP1]], i64 1 +; CHECK-NEXT: ret ptr [[TMP2]] +; + %1 = getelementptr inbounds i64, i64* %p, i64 3 + %2 = getelementptr inbounds %struct.C, %struct.C* %1, i64 1 + ret %struct.C* %2 +} + +; result = (i8*) ((i16*) p + 7) + a +define ptr @opaque(ptr %p, i64 %a) { +; CHECK-LABEL: @opaque( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i16, ptr [[P:%.*]], i64 -1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i64, ptr [[TMP2]], i64 2 +; CHECK-NEXT: ret ptr [[TMP3]] +; + %1 = getelementptr inbounds i16, ptr %p, i64 -1 + %2 = getelementptr inbounds i8, ptr %1, i64 %a + %3 = getelementptr inbounds i64, ptr %2, i64 2 + ret ptr %3 +} + +; result = (i32*) (p - 9) + a +define i32* @bitcast(i8* %p, i64 %a) { +; CHECK-LABEL: @bitcast( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 -1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 -2 +; CHECK-NEXT: ret ptr [[TMP3]] +; + %1 = getelementptr inbounds i8, i8* %p, i64 -1 + %2 = bitcast i8* %1 to i32* + %3 = getelementptr inbounds i32, i32* %2, i64 %a + %4 = getelementptr inbounds i32, i32* %3, i64 -2 + ret i32* %4 +} + +; Last GEP offset is not divisble by first GEP element size. +; result = (i8*) p + 10 +define i8* @bitcastReverse(i64* %p, i64 %a) { +; CHECK-LABEL: @bitcastReverse( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 2 +; CHECK-NEXT: ret ptr [[TMP2]] +; + %1 = getelementptr inbounds i64, i64* %p, i64 1 + %2 = bitcast i64* %1 to i8* + %3 = getelementptr inbounds i8, i8* %2, i64 2 + ret i8* %3 +} + +; result = (i16*) ((i64*) ((i8*) p + a) + (a * b)) + 29 +define i16* @nested(i32* %p, i64 %a, i64 %b) { +; CHECK-LABEL: @nested( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 3 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = mul i64 [[A]], [[B:%.*]] +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds i64, ptr [[TMP2]], i64 [[TMP3]] +; CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds i64, ptr [[TMP4]], i64 4 +; CHECK-NEXT: [[TMP6:%.*]] = getelementptr inbounds i16, ptr [[TMP5]], i64 7 +; CHECK-NEXT: ret ptr [[TMP6]] +; + %1 = getelementptr inbounds i32, i32* %p, i64 3 + %2 = getelementptr inbounds i8, i8* %1, i64 %a + %3 = mul i64 %a, %b + %4 = bitcast i8* %2 to i64* + %5 = getelementptr inbounds i64, i64* %4, i64 %3 + %6 = getelementptr inbounds i64, i64* %5, i64 4 + %7 = bitcast i64* %6 to i16* + %8 = getelementptr inbounds i16, i16* %7, i64 7 + ret i16* %8 +} + +; Offsets of first and last GEP cancel out. +; result = (i8*) p + a +define i8* @zeroSum(i32 *%p, i64 %a) { +; CHECK-LABEL: @zeroSum( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 -4 +; CHECK-NEXT: ret ptr [[TMP3]] +; + %1 = getelementptr inbounds i32, i32* %p, i64 1 + %2 = bitcast i32* %1 to i8* + %3 = getelementptr inbounds i8, i8* %2, i64 %a + %4 = getelementptr inbounds i8, i8* %3, i64 -4 + ret i8* %4 +} + +; Merged index will overflow in the original integer size. +; result = (p + 254) + a +define i32* @indexOverflow(i32* %p, i64 %a) { +; CHECK-LABEL: @indexOverflow( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 127 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 127 +; CHECK-NEXT: ret ptr [[TMP3]] +; + %1 = getelementptr inbounds i32, i32* %p, i8 127 + %2 = getelementptr inbounds i32, i32* %1, i64 %a + %3 = getelementptr inbounds i32, i32* %2, i8 127 + ret i32* %3 +} + +; The optimization still applies if const-indexed GEP have multiple uses, as +; only intermediate GEP are rewritten. +; %4 = (p + 3) + (i32) (p + 1) +; result = %4 + *%4 +define i32* @constIndexGEPMultipleUses(i32* %p, i64 %a) { +; CHECK-LABEL: @constIndexGEPMultipleUses( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1 +; CHECK-NEXT: [[TMP2:%.*]] = ptrtoint ptr [[TMP1]] to i64 +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[TMP2]] +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds i32, ptr [[TMP3]], i64 2 +; CHECK-NEXT: [[TMP5:%.*]] = load i32, ptr [[TMP4]], align 4 +; CHECK-NEXT: [[TMP6:%.*]] = sext i32 [[TMP5]] to i64 +; CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds i32, ptr [[TMP4]], i64 [[TMP6]] +; CHECK-NEXT: ret ptr [[TMP7]] +; + %1 = getelementptr inbounds i32, i32* %p, i64 1 + %2 = ptrtoint i32* %1 to i64 + %3 = getelementptr inbounds i32, i32* %1, i64 %2 + %4 = getelementptr inbounds i32, i32* %3, i64 2 + %5 = load i32, i32* %4 + %6 = getelementptr inbounds i32, i32* %4, i32 %5 + ret i32* %6 +} + +; result = (i32*) ((p[1] + 17) + a) +define i32* @array1([20 x i8]* %p, i64 %a) { +; CHECK-LABEL: @array1( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [20 x i8], ptr [[P:%.*]], i64 1, i64 1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i64, ptr [[TMP2]], i64 2 +; CHECK-NEXT: ret ptr [[TMP3]] +; + %1 = getelementptr inbounds [20 x i8], [20 x i8]* %p, i64 1, i64 1 + %2 = getelementptr inbounds i8, i8* %1, i64 %a + %3 = getelementptr inbounds i64, i64* %2, i64 2 + ret i32* %3 +} + +; result = (i8*) ((i32*) p + a) + 14 +define i8* @array2([7 x i32]* %p, i64 %a) { +; CHECK-LABEL: @array2( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [7 x i32], ptr [[P:%.*]], i64 0, i64 3 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 2 +; CHECK-NEXT: ret ptr [[TMP3]] +; + %1 = getelementptr inbounds [7 x i32], [7 x i32]* %p, i64 0, i64 3 + %2 = getelementptr inbounds i32, i32* %1, i64 %a + %3 = getelementptr inbounds i8, i8* %2, i64 2 + ret i8* %3 +} + +; result = (([3 x i8]*) (p + a))[1] + 17 +define i8* @array3(i64* %p, i64 %a) { +; CHECK-LABEL: @array3( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 2 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i64, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds [3 x i8], ptr [[TMP2]], i64 1, i64 1 +; CHECK-NEXT: ret ptr [[TMP3]] +; + %1 = getelementptr inbounds i64, i64* %p, i64 2 + %2 = getelementptr inbounds i64, i64* %1, i64 %a + %3 = getelementptr inbounds [3 x i8], [3 x i8]* %2, i64 1, i64 1 + ret i8* %3 +} + +; result = ((i8*) p + a) - 4 +define i8* @struct(%struct.A* %p, i64 %a) { +; CHECK-LABEL: @struct( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_A:%.*]], ptr [[P:%.*]], i64 0, i32 1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i8, ptr [[TMP2]], i64 -128 +; CHECK-NEXT: ret ptr [[TMP3]] +; + %1 = getelementptr inbounds %struct.A, %struct.A* %p, i64 0, i32 1 + %2 = getelementptr inbounds i8, i8* %1, i64 %a + %3 = getelementptr inbounds i8, i8* %2, i64 -128 + ret i8* %3 +} + +; result = (i32*) ((p - 4) + a) +define i32* @structReverse(i8* %p, i64 %a) { +; CHECK-LABEL: @structReverse( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i8, ptr [[P:%.*]], i64 -128 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds [[STRUCT_A:%.*]], ptr [[TMP2]], i64 0, i32 1 +; CHECK-NEXT: ret ptr [[TMP3]] +; + %1 = getelementptr inbounds i8, i8* %p, i64 -128 + %2 = getelementptr inbounds i8, i8* %1, i64 %a + %3 = getelementptr inbounds %struct.A, %struct.A* %2, i64 0, i32 1 + ret i32* %3 +} + +; result = ((i8*) &p[0].member2.member0 + 7) + a +define i8* @structStruct(%struct.B* %p, i64 %a) { +; CHECK-LABEL: @structStruct( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_B:%.*]], ptr [[P:%.*]], i64 0, i32 2, i32 0, i64 3 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds [[STRUCT_A:%.*]], ptr [[TMP2]], i64 0, i32 0, i64 4 +; CHECK-NEXT: ret ptr [[TMP3]] +; + %1 = getelementptr inbounds %struct.B, %struct.B* %p, i64 0, i32 2, i32 0, i64 3 + %2 = getelementptr inbounds i8, i8* %1, i64 %a + %3 = getelementptr inbounds %struct.A, %struct.A* %2, i64 0, i32 0, i64 4 + ret i8* %3 +} + +; First GEP offset is not divisible by last GEP's source element size, but first +; GEP points to an array such that the last GEP offset is divisible by the +; array's element size, so the first GEP can be rewritten with an extra index. +; result = (i8*) ((i16*) &p[0].member1 + 4) + a +define i64* @appendIndex(%struct.B* %p, i64 %a) { +; CHECK-LABEL: @appendIndex( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_B:%.*]], ptr [[P:%.*]], i64 0, i32 1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i8, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i64, ptr [[TMP2]], i64 1 +; CHECK-NEXT: ret ptr [[TMP3]] +; + %1 = getelementptr inbounds %struct.B, %struct.B* %p, i64 0, i32 1 + %2 = getelementptr inbounds i8, i8* %1, i64 %a + %3 = getelementptr inbounds i64, i64* %2, i64 1 + ret i64* %3 +} + +; result = (i16*) &((struct.B*) (p + a))[0].member1 + 4 +define ptr @appendIndexReverse(i64* %p, i64 %a) { +; CHECK-LABEL: @appendIndexReverse( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i64, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds [[STRUCT_B:%.*]], ptr [[TMP2]], i64 0, i32 1 +; CHECK-NEXT: ret ptr [[TMP3]] +; + %1 = getelementptr inbounds i64, i64* %p, i64 1 + %2 = getelementptr inbounds i64, i64* %1, i64 %a + %3 = getelementptr inbounds %struct.B, %struct.B* %2, i64 0, i32 1 + ret ptr %3 +} + +; Middle const-indexed GEP can't be merged to either first or last GEP, so it +; is being skipped. +; result = (struct.C*) (([5 x i64]*) ((p + 4) + a) + 1) + b +define ptr @skipMiddleGEP(%struct.C* %p, i64 %a, i64 %b) { +; CHECK-LABEL: @skipMiddleGEP( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[P:%.*]], i64 1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_C]], ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds [5 x i64], ptr [[TMP2]], i64 1 +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds [[STRUCT_C]], ptr [[TMP3]], i64 [[B:%.*]] +; CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds [[STRUCT_C]], ptr [[TMP4]], i64 3 +; CHECK-NEXT: ret ptr [[TMP5]] +; + %1 = getelementptr inbounds %struct.C, %struct.C* %p, i64 1 + %2 = getelementptr inbounds %struct.C, %struct.C* %1, i64 %a + %3 = getelementptr inbounds [5 x i64], [5 x i64]* %2, i64 1 + %4 = getelementptr inbounds %struct.C, %struct.C* %3, i64 %b + %5 = getelementptr inbounds %struct.C, %struct.C* %4, i64 3 + ret ptr %5 +} + +; Negative test, can't find a pair of const-indexed GEP. +define i32* @nonConst1(i32* %p, i64 %a) { +; CHECK-LABEL: @nonConst1( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: ret ptr [[TMP2]] +; + %1 = getelementptr inbounds i32, i32* %p, i64 1 + %2 = getelementptr inbounds i32, i32* %1, i64 %a + ret i32* %2 +} + +; Negative test, other arithmetic operations are involved in the GEP chain +define i32* @nonConst2(i32* %p, i64 %a) { +; CHECK-LABEL: @nonConst2( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1 +; CHECK-NEXT: [[TMP2:%.*]] = ptrtoint ptr [[TMP1]] to i64 +; CHECK-NEXT: [[TMP3:%.*]] = mul nsw i64 [[TMP2]], [[A:%.*]] +; CHECK-NEXT: [[TMP4:%.*]] = inttoptr i64 [[TMP3]] to ptr +; CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds i32, ptr [[TMP4]], i64 2 +; CHECK-NEXT: ret ptr [[TMP5]] +; + %1 = getelementptr inbounds i32, i32* %p, i64 1 + %2 = ptrtoint i32* %1 to i64 + %3 = mul nsw i64 %2, %a + %4 = inttoptr i64 %3 to i32* + %5 = getelementptr inbounds i32, i32* %4, i64 2 + ret i32* %5 +} + +; Negative test, intermediate GEP has more than 1 use. +define i32* @intermediateMoreThanOneUse(i32* %p, i64 %a) { +; CHECK-LABEL: @intermediateMoreThanOneUse( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i32, ptr [[P:%.*]], i64 1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i32, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = ptrtoint ptr [[TMP2]] to i64 +; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 [[TMP3]] +; CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds i32, ptr [[TMP4]], i64 2 +; CHECK-NEXT: ret ptr [[TMP5]] +; + %1 = getelementptr inbounds i32, i32* %p, i64 1 + %2 = getelementptr inbounds i32, i32* %1, i64 %a + %3 = ptrtoint i32* %2 to i64 + %4 = getelementptr inbounds i32, i32* %2, i64 %3 + %5 = getelementptr inbounds i32, i32* %4, i64 2 + ret i32* %5 +} + +; Negative test, cannot guarantee arithmetic validity if GEP is not in bounds. +define i32* @notInBounds(i32* %p, i64 %a) { +; CHECK-LABEL: @notInBounds( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr i32, ptr [[P:%.*]], i64 1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i32, ptr [[TMP2]], i64 2 +; CHECK-NEXT: ret ptr [[TMP3]] +; + %1 = getelementptr i32, i32* %p, i64 1 + %2 = getelementptr i32, i32* %1, i64 %a + %3 = getelementptr inbounds i32, i32* %2, i64 2 + ret i32* %3 +} + +; Negative test, neither the last GEP offset is divisble by first GEP size, nor +; the other way around. +define %struct.C* @offsetNotDivisible(i64* %p, i64 %a) { +; CHECK-LABEL: @offsetNotDivisible( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds i64, ptr [[P:%.*]], i64 1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[TMP1]], i64 1 +; CHECK-NEXT: ret ptr [[TMP2]] +; + %1 = getelementptr inbounds i64, i64* %p, i64 1 + %2 = getelementptr inbounds %struct.C, %struct.C* %1, i64 1 + ret %struct.C* %2 +} + +; Negative test, first GEP offset is not divisible by last GEP size, and first +; GEP is a pointer to struct member. +define i64* @structNotDivisible(%struct.C* %p, i64 %a) { +; CHECK-LABEL: @structNotDivisible( +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds [[STRUCT_C:%.*]], ptr [[P:%.*]], i64 0, i32 1 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds i16, ptr [[TMP1]], i64 [[A:%.*]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr inbounds i64, ptr [[TMP2]], i64 1 +; CHECK-NEXT: ret ptr [[TMP3]] +; + %1 = getelementptr inbounds %struct.C, %struct.C* %p, i64 0, i32 1 + %2 = getelementptr inbounds i16, i16* %1, i64 %a + %3 = getelementptr inbounds i64, i64* %2, i64 1 + ret i64* %3 +}