Index: llvm/trunk/lib/Transforms/InstCombine/InstructionCombining.cpp =================================================================== --- llvm/trunk/lib/Transforms/InstCombine/InstructionCombining.cpp +++ llvm/trunk/lib/Transforms/InstCombine/InstructionCombining.cpp @@ -144,12 +144,20 @@ /// We don't want to convert from a legal to an illegal type or from a smaller /// to a larger illegal type. A width of '1' is always treated as a legal type /// because i1 is a fundamental type in IR, and there are many specialized -/// optimizations for i1 types. +/// optimizations for i1 types. Widths of 8, 16 or 32 are equally treated as +/// legal to convert to, in order to open up more combining opportunities. +/// NOTE: this treats i8, i16 and i32 specially, due to them being so common +/// from frontend languages. bool InstCombiner::shouldChangeType(unsigned FromWidth, unsigned ToWidth) const { bool FromLegal = FromWidth == 1 || DL.isLegalInteger(FromWidth); bool ToLegal = ToWidth == 1 || DL.isLegalInteger(ToWidth); + // Convert to widths of 8, 16 or 32 even if they are not legal types. Only + // shrink types, to prevent infinite loops. + if (ToWidth < FromWidth && (ToWidth == 8 || ToWidth == 16 || ToWidth == 32)) + return true; + // If this is a legal integer from type, and the result would be an illegal // type, don't do the transformation. if (FromLegal && !ToLegal) Index: llvm/trunk/test/Transforms/InstCombine/and-narrow.ll =================================================================== --- llvm/trunk/test/Transforms/InstCombine/and-narrow.ll +++ llvm/trunk/test/Transforms/InstCombine/and-narrow.ll @@ -1,21 +1,15 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt < %s -instcombine -data-layout="n8:16:32" -S | FileCheck %s --check-prefix=ALL --check-prefix=LEGAL8 -; RUN: opt < %s -instcombine -data-layout="n16" -S | FileCheck %s --check-prefix=ALL --check-prefix=LEGAL16 +; RUN: opt < %s -instcombine -data-layout="n8:16:32" -S | FileCheck %s +; RUN: opt < %s -instcombine -data-layout="n16" -S | FileCheck %s ; PR35792 - https://bugs.llvm.org/show_bug.cgi?id=35792 define i16 @zext_add(i8 %x) { -; LEGAL8-LABEL: @zext_add( -; LEGAL8-NEXT: [[TMP1:%.*]] = add i8 [[X:%.*]], 44 -; LEGAL8-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X]] -; LEGAL8-NEXT: [[R:%.*]] = zext i8 [[TMP2]] to i16 -; LEGAL8-NEXT: ret i16 [[R]] -; -; LEGAL16-LABEL: @zext_add( -; LEGAL16-NEXT: [[Z:%.*]] = zext i8 [[X:%.*]] to i16 -; LEGAL16-NEXT: [[B:%.*]] = add nuw nsw i16 [[Z]], 44 -; LEGAL16-NEXT: [[R:%.*]] = and i16 [[B]], [[Z]] -; LEGAL16-NEXT: ret i16 [[R]] +; CHECK-LABEL: @zext_add( +; CHECK-NEXT: [[TMP1:%.*]] = add i8 [[X:%.*]], 44 +; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = zext i8 [[TMP2]] to i16 +; CHECK-NEXT: ret i16 [[R]] ; %z = zext i8 %x to i16 %b = add i16 %z, 44 @@ -24,17 +18,11 @@ } define i16 @zext_sub(i8 %x) { -; LEGAL8-LABEL: @zext_sub( -; LEGAL8-NEXT: [[TMP1:%.*]] = sub i8 -5, [[X:%.*]] -; LEGAL8-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X]] -; LEGAL8-NEXT: [[R:%.*]] = zext i8 [[TMP2]] to i16 -; LEGAL8-NEXT: ret i16 [[R]] -; -; LEGAL16-LABEL: @zext_sub( -; LEGAL16-NEXT: [[Z:%.*]] = zext i8 [[X:%.*]] to i16 -; LEGAL16-NEXT: [[B:%.*]] = sub nsw i16 251, [[Z]] -; LEGAL16-NEXT: [[R:%.*]] = and i16 [[B]], [[Z]] -; LEGAL16-NEXT: ret i16 [[R]] +; CHECK-LABEL: @zext_sub( +; CHECK-NEXT: [[TMP1:%.*]] = sub i8 -5, [[X:%.*]] +; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = zext i8 [[TMP2]] to i16 +; CHECK-NEXT: ret i16 [[R]] ; %z = zext i8 %x to i16 %b = sub i16 -5, %z @@ -43,17 +31,11 @@ } define i16 @zext_mul(i8 %x) { -; LEGAL8-LABEL: @zext_mul( -; LEGAL8-NEXT: [[TMP1:%.*]] = mul i8 [[X:%.*]], 3 -; LEGAL8-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X]] -; LEGAL8-NEXT: [[R:%.*]] = zext i8 [[TMP2]] to i16 -; LEGAL8-NEXT: ret i16 [[R]] -; -; LEGAL16-LABEL: @zext_mul( -; LEGAL16-NEXT: [[Z:%.*]] = zext i8 [[X:%.*]] to i16 -; LEGAL16-NEXT: [[B:%.*]] = mul nuw nsw i16 [[Z]], 3 -; LEGAL16-NEXT: [[R:%.*]] = and i16 [[B]], [[Z]] -; LEGAL16-NEXT: ret i16 [[R]] +; CHECK-LABEL: @zext_mul( +; CHECK-NEXT: [[TMP1:%.*]] = mul i8 [[X:%.*]], 3 +; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = zext i8 [[TMP2]] to i16 +; CHECK-NEXT: ret i16 [[R]] ; %z = zext i8 %x to i16 %b = mul i16 %z, 3 @@ -62,17 +44,11 @@ } define i16 @zext_lshr(i8 %x) { -; LEGAL8-LABEL: @zext_lshr( -; LEGAL8-NEXT: [[TMP1:%.*]] = lshr i8 [[X:%.*]], 4 -; LEGAL8-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X]] -; LEGAL8-NEXT: [[R:%.*]] = zext i8 [[TMP2]] to i16 -; LEGAL8-NEXT: ret i16 [[R]] -; -; LEGAL16-LABEL: @zext_lshr( -; LEGAL16-NEXT: [[Z:%.*]] = zext i8 [[X:%.*]] to i16 -; LEGAL16-NEXT: [[B:%.*]] = lshr i16 [[Z]], 4 -; LEGAL16-NEXT: [[R:%.*]] = and i16 [[B]], [[Z]] -; LEGAL16-NEXT: ret i16 [[R]] +; CHECK-LABEL: @zext_lshr( +; CHECK-NEXT: [[TMP1:%.*]] = lshr i8 [[X:%.*]], 4 +; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = zext i8 [[TMP2]] to i16 +; CHECK-NEXT: ret i16 [[R]] ; %z = zext i8 %x to i16 %b = lshr i16 %z, 4 @@ -81,17 +57,11 @@ } define i16 @zext_ashr(i8 %x) { -; LEGAL8-LABEL: @zext_ashr( -; LEGAL8-NEXT: [[TMP1:%.*]] = lshr i8 [[X:%.*]], 2 -; LEGAL8-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X]] -; LEGAL8-NEXT: [[R:%.*]] = zext i8 [[TMP2]] to i16 -; LEGAL8-NEXT: ret i16 [[R]] -; -; LEGAL16-LABEL: @zext_ashr( -; LEGAL16-NEXT: [[Z:%.*]] = zext i8 [[X:%.*]] to i16 -; LEGAL16-NEXT: [[TMP1:%.*]] = lshr i16 [[Z]], 2 -; LEGAL16-NEXT: [[R:%.*]] = and i16 [[TMP1]], [[Z]] -; LEGAL16-NEXT: ret i16 [[R]] +; CHECK-LABEL: @zext_ashr( +; CHECK-NEXT: [[TMP1:%.*]] = lshr i8 [[X:%.*]], 2 +; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = zext i8 [[TMP2]] to i16 +; CHECK-NEXT: ret i16 [[R]] ; %z = zext i8 %x to i16 %b = ashr i16 %z, 2 @@ -100,17 +70,11 @@ } define i16 @zext_shl(i8 %x) { -; LEGAL8-LABEL: @zext_shl( -; LEGAL8-NEXT: [[TMP1:%.*]] = shl i8 [[X:%.*]], 3 -; LEGAL8-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X]] -; LEGAL8-NEXT: [[R:%.*]] = zext i8 [[TMP2]] to i16 -; LEGAL8-NEXT: ret i16 [[R]] -; -; LEGAL16-LABEL: @zext_shl( -; LEGAL16-NEXT: [[Z:%.*]] = zext i8 [[X:%.*]] to i16 -; LEGAL16-NEXT: [[B:%.*]] = shl nuw nsw i16 [[Z]], 3 -; LEGAL16-NEXT: [[R:%.*]] = and i16 [[B]], [[Z]] -; LEGAL16-NEXT: ret i16 [[R]] +; CHECK-LABEL: @zext_shl( +; CHECK-NEXT: [[TMP1:%.*]] = shl i8 [[X:%.*]], 3 +; CHECK-NEXT: [[TMP2:%.*]] = and i8 [[TMP1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = zext i8 [[TMP2]] to i16 +; CHECK-NEXT: ret i16 [[R]] ; %z = zext i8 %x to i16 %b = shl i16 %z, 3 @@ -119,11 +83,11 @@ } define <2 x i16> @zext_add_vec(<2 x i8> %x) { -; ALL-LABEL: @zext_add_vec( -; ALL-NEXT: [[TMP1:%.*]] = add <2 x i8> [[X:%.*]], -; ALL-NEXT: [[TMP2:%.*]] = and <2 x i8> [[TMP1]], [[X]] -; ALL-NEXT: [[R:%.*]] = zext <2 x i8> [[TMP2]] to <2 x i16> -; ALL-NEXT: ret <2 x i16> [[R]] +; CHECK-LABEL: @zext_add_vec( +; CHECK-NEXT: [[TMP1:%.*]] = add <2 x i8> [[X:%.*]], +; CHECK-NEXT: [[TMP2:%.*]] = and <2 x i8> [[TMP1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = zext <2 x i8> [[TMP2]] to <2 x i16> +; CHECK-NEXT: ret <2 x i16> [[R]] ; %z = zext <2 x i8> %x to <2 x i16> %b = add <2 x i16> %z, @@ -132,11 +96,11 @@ } define <2 x i16> @zext_sub_vec(<2 x i8> %x) { -; ALL-LABEL: @zext_sub_vec( -; ALL-NEXT: [[TMP1:%.*]] = sub <2 x i8> , [[X:%.*]] -; ALL-NEXT: [[TMP2:%.*]] = and <2 x i8> [[TMP1]], [[X]] -; ALL-NEXT: [[R:%.*]] = zext <2 x i8> [[TMP2]] to <2 x i16> -; ALL-NEXT: ret <2 x i16> [[R]] +; CHECK-LABEL: @zext_sub_vec( +; CHECK-NEXT: [[TMP1:%.*]] = sub <2 x i8> , [[X:%.*]] +; CHECK-NEXT: [[TMP2:%.*]] = and <2 x i8> [[TMP1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = zext <2 x i8> [[TMP2]] to <2 x i16> +; CHECK-NEXT: ret <2 x i16> [[R]] ; %z = zext <2 x i8> %x to <2 x i16> %b = sub <2 x i16> , %z @@ -145,11 +109,11 @@ } define <2 x i16> @zext_mul_vec(<2 x i8> %x) { -; ALL-LABEL: @zext_mul_vec( -; ALL-NEXT: [[TMP1:%.*]] = mul <2 x i8> [[X:%.*]], -; ALL-NEXT: [[TMP2:%.*]] = and <2 x i8> [[TMP1]], [[X]] -; ALL-NEXT: [[R:%.*]] = zext <2 x i8> [[TMP2]] to <2 x i16> -; ALL-NEXT: ret <2 x i16> [[R]] +; CHECK-LABEL: @zext_mul_vec( +; CHECK-NEXT: [[TMP1:%.*]] = mul <2 x i8> [[X:%.*]], +; CHECK-NEXT: [[TMP2:%.*]] = and <2 x i8> [[TMP1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = zext <2 x i8> [[TMP2]] to <2 x i16> +; CHECK-NEXT: ret <2 x i16> [[R]] ; %z = zext <2 x i8> %x to <2 x i16> %b = mul <2 x i16> %z, @@ -158,11 +122,11 @@ } define <2 x i16> @zext_lshr_vec(<2 x i8> %x) { -; ALL-LABEL: @zext_lshr_vec( -; ALL-NEXT: [[TMP1:%.*]] = lshr <2 x i8> [[X:%.*]], -; ALL-NEXT: [[TMP2:%.*]] = and <2 x i8> [[TMP1]], [[X]] -; ALL-NEXT: [[R:%.*]] = zext <2 x i8> [[TMP2]] to <2 x i16> -; ALL-NEXT: ret <2 x i16> [[R]] +; CHECK-LABEL: @zext_lshr_vec( +; CHECK-NEXT: [[TMP1:%.*]] = lshr <2 x i8> [[X:%.*]], +; CHECK-NEXT: [[TMP2:%.*]] = and <2 x i8> [[TMP1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = zext <2 x i8> [[TMP2]] to <2 x i16> +; CHECK-NEXT: ret <2 x i16> [[R]] ; %z = zext <2 x i8> %x to <2 x i16> %b = lshr <2 x i16> %z, @@ -171,11 +135,11 @@ } define <2 x i16> @zext_ashr_vec(<2 x i8> %x) { -; ALL-LABEL: @zext_ashr_vec( -; ALL-NEXT: [[TMP1:%.*]] = lshr <2 x i8> [[X:%.*]], -; ALL-NEXT: [[TMP2:%.*]] = and <2 x i8> [[TMP1]], [[X]] -; ALL-NEXT: [[R:%.*]] = zext <2 x i8> [[TMP2]] to <2 x i16> -; ALL-NEXT: ret <2 x i16> [[R]] +; CHECK-LABEL: @zext_ashr_vec( +; CHECK-NEXT: [[TMP1:%.*]] = lshr <2 x i8> [[X:%.*]], +; CHECK-NEXT: [[TMP2:%.*]] = and <2 x i8> [[TMP1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = zext <2 x i8> [[TMP2]] to <2 x i16> +; CHECK-NEXT: ret <2 x i16> [[R]] ; %z = zext <2 x i8> %x to <2 x i16> %b = ashr <2 x i16> %z, @@ -184,11 +148,11 @@ } define <2 x i16> @zext_shl_vec(<2 x i8> %x) { -; ALL-LABEL: @zext_shl_vec( -; ALL-NEXT: [[TMP1:%.*]] = shl <2 x i8> [[X:%.*]], -; ALL-NEXT: [[TMP2:%.*]] = and <2 x i8> [[TMP1]], [[X]] -; ALL-NEXT: [[R:%.*]] = zext <2 x i8> [[TMP2]] to <2 x i16> -; ALL-NEXT: ret <2 x i16> [[R]] +; CHECK-LABEL: @zext_shl_vec( +; CHECK-NEXT: [[TMP1:%.*]] = shl <2 x i8> [[X:%.*]], +; CHECK-NEXT: [[TMP2:%.*]] = and <2 x i8> [[TMP1]], [[X]] +; CHECK-NEXT: [[R:%.*]] = zext <2 x i8> [[TMP2]] to <2 x i16> +; CHECK-NEXT: ret <2 x i16> [[R]] ; %z = zext <2 x i8> %x to <2 x i16> %b = shl <2 x i16> %z, @@ -199,11 +163,11 @@ ; Don't create poison by narrowing a shift below the shift amount. define <2 x i16> @zext_lshr_vec_overshift(<2 x i8> %x) { -; ALL-LABEL: @zext_lshr_vec_overshift( -; ALL-NEXT: [[Z:%.*]] = zext <2 x i8> [[X:%.*]] to <2 x i16> -; ALL-NEXT: [[B:%.*]] = lshr <2 x i16> [[Z]], -; ALL-NEXT: [[R:%.*]] = and <2 x i16> [[B]], [[Z]] -; ALL-NEXT: ret <2 x i16> [[R]] +; CHECK-LABEL: @zext_lshr_vec_overshift( +; CHECK-NEXT: [[Z:%.*]] = zext <2 x i8> [[X:%.*]] to <2 x i16> +; CHECK-NEXT: [[B:%.*]] = lshr <2 x i16> [[Z]], +; CHECK-NEXT: [[R:%.*]] = and <2 x i16> [[B]], [[Z]] +; CHECK-NEXT: ret <2 x i16> [[R]] ; %z = zext <2 x i8> %x to <2 x i16> %b = lshr <2 x i16> %z, @@ -214,11 +178,11 @@ ; Don't create poison by narrowing a shift below the shift amount. define <2 x i16> @zext_shl_vec_overshift(<2 x i8> %x) { -; ALL-LABEL: @zext_shl_vec_overshift( -; ALL-NEXT: [[Z:%.*]] = zext <2 x i8> [[X:%.*]] to <2 x i16> -; ALL-NEXT: [[B:%.*]] = shl <2 x i16> [[Z]], -; ALL-NEXT: [[R:%.*]] = and <2 x i16> [[B]], [[Z]] -; ALL-NEXT: ret <2 x i16> [[R]] +; CHECK-LABEL: @zext_shl_vec_overshift( +; CHECK-NEXT: [[Z:%.*]] = zext <2 x i8> [[X:%.*]] to <2 x i16> +; CHECK-NEXT: [[B:%.*]] = shl <2 x i16> [[Z]], +; CHECK-NEXT: [[R:%.*]] = and <2 x i16> [[B]], [[Z]] +; CHECK-NEXT: ret <2 x i16> [[R]] ; %z = zext <2 x i8> %x to <2 x i16> %b = shl <2 x i16> %z, Index: llvm/trunk/test/Transforms/InstCombine/phi-timeout.ll =================================================================== --- llvm/trunk/test/Transforms/InstCombine/phi-timeout.ll +++ llvm/trunk/test/Transforms/InstCombine/phi-timeout.ll @@ -0,0 +1,47 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instcombine -S -debug 2>&1 | FileCheck %s +target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64" + +; We are really checking that this doesn't loop forever. We would never +; actually get to the checks here if it did. + +define void @timeout(i16* nocapture readonly %cinfo) { +; CHECK-LABEL: @timeout( +; CHECK-NEXT: entry: +; CHECK-NEXT: br label [[FOR_BODY:%.*]] +; CHECK: for.body: +; CHECK-NEXT: [[ARRAYIDX15:%.*]] = getelementptr inbounds i16, i16* [[CINFO:%.*]], i32 2 +; CHECK-NEXT: [[L:%.*]] = load i16, i16* [[ARRAYIDX15]], align 2 +; CHECK-NEXT: [[CMP17:%.*]] = icmp eq i16 [[L]], 0 +; CHECK-NEXT: [[EXTRACT_T1:%.*]] = trunc i16 [[L]] to i8 +; CHECK-NEXT: br i1 [[CMP17]], label [[IF_THEN:%.*]], label [[IF_END:%.*]] +; CHECK: if.then: +; CHECK-NEXT: [[DOTPRE:%.*]] = load i16, i16* [[ARRAYIDX15]], align 2 +; CHECK-NEXT: [[EXTRACT_T:%.*]] = trunc i16 [[DOTPRE]] to i8 +; CHECK-NEXT: br label [[IF_END]] +; CHECK: if.end: +; CHECK-NEXT: [[P_OFF0:%.*]] = phi i8 [ [[EXTRACT_T]], [[IF_THEN]] ], [ [[EXTRACT_T1]], [[FOR_BODY]] ] +; CHECK-NEXT: [[SUB:%.*]] = add i8 [[P_OFF0]], -1 +; CHECK-NEXT: store i8 [[SUB]], i8* undef, align 1 +; CHECK-NEXT: br label [[FOR_BODY]] +; +entry: + br label %for.body + +for.body: + %arrayidx15 = getelementptr inbounds i16, i16* %cinfo, i32 2 + %l = load i16, i16* %arrayidx15, align 2 + %cmp17 = icmp eq i16 %l, 0 + br i1 %cmp17, label %if.then, label %if.end + +if.then: + %.pre = load i16, i16* %arrayidx15, align 2 + br label %if.end + +if.end: + %p = phi i16 [ %.pre, %if.then ], [ %l, %for.body ] + %conv19 = trunc i16 %p to i8 + %sub = add i8 %conv19, -1 + store i8 %sub, i8* undef, align 1 + br label %for.body +} Index: llvm/trunk/test/Transforms/InstCombine/should-change-type.ll =================================================================== --- llvm/trunk/test/Transforms/InstCombine/should-change-type.ll +++ llvm/trunk/test/Transforms/InstCombine/should-change-type.ll @@ -0,0 +1,57 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -instcombine -S | FileCheck %s +target datalayout = "n64" + +; Tests for removing zext/trunc from/to i8, i16 and i32, even if it is +; not a legal type. + +define i8 @test1(i8 %x, i8 %y) { +; CHECK-LABEL: @test1( +; CHECK-NEXT: [[C:%.*]] = add i8 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret i8 [[C]] +; + %xz = zext i8 %x to i64 + %yz = zext i8 %y to i64 + %c = add i64 %xz, %yz + %d = trunc i64 %c to i8 + ret i8 %d +} + +define i16 @test2(i16 %x, i16 %y) { +; CHECK-LABEL: @test2( +; CHECK-NEXT: [[C:%.*]] = add i16 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret i16 [[C]] +; + %xz = zext i16 %x to i64 + %yz = zext i16 %y to i64 + %c = add i64 %xz, %yz + %d = trunc i64 %c to i16 + ret i16 %d +} + +define i32 @test3(i32 %x, i32 %y) { +; CHECK-LABEL: @test3( +; CHECK-NEXT: [[C:%.*]] = add i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: ret i32 [[C]] +; + %xz = zext i32 %x to i64 + %yz = zext i32 %y to i64 + %c = add i64 %xz, %yz + %d = trunc i64 %c to i32 + ret i32 %d +} + +define i9 @test4(i9 %x, i9 %y) { +; CHECK-LABEL: @test4( +; CHECK-NEXT: [[XZ:%.*]] = zext i9 [[X:%.*]] to i64 +; CHECK-NEXT: [[YZ:%.*]] = zext i9 [[Y:%.*]] to i64 +; CHECK-NEXT: [[C:%.*]] = add nuw nsw i64 [[XZ]], [[YZ]] +; CHECK-NEXT: [[D:%.*]] = trunc i64 [[C]] to i9 +; CHECK-NEXT: ret i9 [[D]] +; + %xz = zext i9 %x to i64 + %yz = zext i9 %y to i64 + %c = add i64 %xz, %yz + %d = trunc i64 %c to i9 + ret i9 %d +}