Fixes #84831 When matching carry pattern with `getAsCarry`, it may produce different type of carryout. This patch checks such case and does early exit. I'm new to DAG, any suggestion is appreciated.
1516 lines
49 KiB
LLVM
1516 lines
49 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
|
; RUN: llc < %s -mtriple=x86_64-unknown | FileCheck %s
|
|
|
|
declare { i8, i64 } @llvm.x86.addcarry.64(i8, i64, i64)
|
|
declare { i64, i1 } @llvm.uadd.with.overflow.i64(i64, i64) #1
|
|
declare { i64, i1 } @llvm.usub.with.overflow.i64(i64, i64) #1
|
|
declare { i128, i1 } @llvm.sadd.with.overflow.i128(i128, i128)
|
|
|
|
define i128 @add128(i128 %a, i128 %b) nounwind {
|
|
; CHECK-LABEL: add128:
|
|
; CHECK: # %bb.0: # %entry
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: addq %rdx, %rax
|
|
; CHECK-NEXT: adcq %rcx, %rsi
|
|
; CHECK-NEXT: movq %rsi, %rdx
|
|
; CHECK-NEXT: retq
|
|
entry:
|
|
%0 = add i128 %a, %b
|
|
ret i128 %0
|
|
}
|
|
|
|
define void @add128_rmw(ptr %a, i128 %b) nounwind {
|
|
; CHECK-LABEL: add128_rmw:
|
|
; CHECK: # %bb.0: # %entry
|
|
; CHECK-NEXT: addq %rsi, (%rdi)
|
|
; CHECK-NEXT: adcq %rdx, 8(%rdi)
|
|
; CHECK-NEXT: retq
|
|
entry:
|
|
%0 = load i128, ptr %a
|
|
%1 = add i128 %0, %b
|
|
store i128 %1, ptr %a
|
|
ret void
|
|
}
|
|
|
|
define void @add128_rmw2(i128 %a, ptr %b) nounwind {
|
|
; CHECK-LABEL: add128_rmw2:
|
|
; CHECK: # %bb.0: # %entry
|
|
; CHECK-NEXT: addq %rdi, (%rdx)
|
|
; CHECK-NEXT: adcq %rsi, 8(%rdx)
|
|
; CHECK-NEXT: retq
|
|
entry:
|
|
%0 = load i128, ptr %b
|
|
%1 = add i128 %a, %0
|
|
store i128 %1, ptr %b
|
|
ret void
|
|
}
|
|
|
|
define i256 @add256(i256 %a, i256 %b) nounwind {
|
|
; CHECK-LABEL: add256:
|
|
; CHECK: # %bb.0: # %entry
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: addq {{[0-9]+}}(%rsp), %rsi
|
|
; CHECK-NEXT: adcq {{[0-9]+}}(%rsp), %rdx
|
|
; CHECK-NEXT: adcq {{[0-9]+}}(%rsp), %rcx
|
|
; CHECK-NEXT: adcq {{[0-9]+}}(%rsp), %r8
|
|
; CHECK-NEXT: movq %rcx, 16(%rdi)
|
|
; CHECK-NEXT: movq %rdx, 8(%rdi)
|
|
; CHECK-NEXT: movq %rsi, (%rdi)
|
|
; CHECK-NEXT: movq %r8, 24(%rdi)
|
|
; CHECK-NEXT: retq
|
|
entry:
|
|
%0 = add i256 %a, %b
|
|
ret i256 %0
|
|
}
|
|
|
|
define void @add256_rmw(ptr %a, i256 %b) nounwind {
|
|
; CHECK-LABEL: add256_rmw:
|
|
; CHECK: # %bb.0: # %entry
|
|
; CHECK-NEXT: addq %rsi, (%rdi)
|
|
; CHECK-NEXT: adcq %rdx, 8(%rdi)
|
|
; CHECK-NEXT: adcq %rcx, 16(%rdi)
|
|
; CHECK-NEXT: adcq %r8, 24(%rdi)
|
|
; CHECK-NEXT: retq
|
|
entry:
|
|
%0 = load i256, ptr %a
|
|
%1 = add i256 %0, %b
|
|
store i256 %1, ptr %a
|
|
ret void
|
|
}
|
|
|
|
define void @add256_rmw2(i256 %a, ptr %b) nounwind {
|
|
; CHECK-LABEL: add256_rmw2:
|
|
; CHECK: # %bb.0: # %entry
|
|
; CHECK-NEXT: addq %rdi, (%r8)
|
|
; CHECK-NEXT: adcq %rsi, 8(%r8)
|
|
; CHECK-NEXT: adcq %rdx, 16(%r8)
|
|
; CHECK-NEXT: adcq %rcx, 24(%r8)
|
|
; CHECK-NEXT: retq
|
|
entry:
|
|
%0 = load i256, ptr %b
|
|
%1 = add i256 %a, %0
|
|
store i256 %1, ptr %b
|
|
ret void
|
|
}
|
|
|
|
define void @a(ptr nocapture %s, ptr nocapture %t, i64 %a, i64 %b, i64 %c) nounwind {
|
|
; CHECK-LABEL: a:
|
|
; CHECK: # %bb.0: # %entry
|
|
; CHECK-NEXT: addq %rcx, %rdx
|
|
; CHECK-NEXT: adcq $0, %r8
|
|
; CHECK-NEXT: movq %r8, (%rdi)
|
|
; CHECK-NEXT: movq %rdx, (%rsi)
|
|
; CHECK-NEXT: retq
|
|
entry:
|
|
%0 = zext i64 %a to i128
|
|
%1 = zext i64 %b to i128
|
|
%2 = add i128 %1, %0
|
|
%3 = zext i64 %c to i128
|
|
%4 = shl i128 %3, 64
|
|
%5 = add i128 %4, %2
|
|
%6 = lshr i128 %5, 64
|
|
%7 = trunc i128 %6 to i64
|
|
store i64 %7, ptr %s, align 8
|
|
%8 = trunc i128 %2 to i64
|
|
store i64 %8, ptr %t, align 8
|
|
ret void
|
|
}
|
|
|
|
define void @b(ptr nocapture %r, i64 %a, i64 %b, i32 %c) nounwind {
|
|
; CHECK-LABEL: b:
|
|
; CHECK: # %bb.0: # %entry
|
|
; CHECK-NEXT: addq %rdx, %rsi
|
|
; CHECK-NEXT: adcl $0, %ecx
|
|
; CHECK-NEXT: movl %ecx, (%rdi)
|
|
; CHECK-NEXT: retq
|
|
entry:
|
|
%0 = zext i64 %a to i128
|
|
%1 = zext i64 %b to i128
|
|
%2 = zext i32 %c to i128
|
|
%3 = add i128 %1, %0
|
|
%4 = lshr i128 %3, 64
|
|
%5 = add i128 %4, %2
|
|
%6 = trunc i128 %5 to i32
|
|
store i32 %6, ptr %r, align 4
|
|
ret void
|
|
}
|
|
|
|
define void @c(ptr nocapture %r, i64 %a, i64 %b, i16 %c) nounwind {
|
|
; CHECK-LABEL: c:
|
|
; CHECK: # %bb.0: # %entry
|
|
; CHECK-NEXT: addq %rdx, %rsi
|
|
; CHECK-NEXT: adcw $0, %cx
|
|
; CHECK-NEXT: movw %cx, (%rdi)
|
|
; CHECK-NEXT: retq
|
|
entry:
|
|
%0 = zext i64 %a to i128
|
|
%1 = zext i64 %b to i128
|
|
%2 = zext i16 %c to i128
|
|
%3 = add i128 %1, %0
|
|
%4 = lshr i128 %3, 64
|
|
%5 = add i128 %4, %2
|
|
%6 = trunc i128 %5 to i16
|
|
store i16 %6, ptr %r, align 4
|
|
ret void
|
|
}
|
|
|
|
define void @d(ptr nocapture %r, i64 %a, i64 %b, i8 %c) nounwind {
|
|
; CHECK-LABEL: d:
|
|
; CHECK: # %bb.0: # %entry
|
|
; CHECK-NEXT: addq %rdx, %rsi
|
|
; CHECK-NEXT: adcb $0, %cl
|
|
; CHECK-NEXT: movb %cl, (%rdi)
|
|
; CHECK-NEXT: retq
|
|
entry:
|
|
%0 = zext i64 %a to i128
|
|
%1 = zext i64 %b to i128
|
|
%2 = zext i8 %c to i128
|
|
%3 = add i128 %1, %0
|
|
%4 = lshr i128 %3, 64
|
|
%5 = add i128 %4, %2
|
|
%6 = trunc i128 %5 to i8
|
|
store i8 %6, ptr %r, align 4
|
|
ret void
|
|
}
|
|
|
|
define i8 @e(ptr nocapture %a, i32 %b) nounwind {
|
|
; CHECK-LABEL: e:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: # kill: def $esi killed $esi def $rsi
|
|
; CHECK-NEXT: movl (%rdi), %ecx
|
|
; CHECK-NEXT: leal (%rsi,%rcx), %edx
|
|
; CHECK-NEXT: addl %esi, %edx
|
|
; CHECK-NEXT: setb %al
|
|
; CHECK-NEXT: addl %ecx, %esi
|
|
; CHECK-NEXT: movl %edx, (%rdi)
|
|
; CHECK-NEXT: adcb $0, %al
|
|
; CHECK-NEXT: retq
|
|
%1 = load i32, ptr %a, align 4
|
|
%2 = add i32 %1, %b
|
|
%3 = icmp ult i32 %2, %b
|
|
%4 = zext i1 %3 to i8
|
|
%5 = add i32 %2, %b
|
|
store i32 %5, ptr %a, align 4
|
|
%6 = icmp ult i32 %5, %b
|
|
%7 = zext i1 %6 to i8
|
|
%8 = add nuw nsw i8 %7, %4
|
|
ret i8 %8
|
|
}
|
|
|
|
%scalar = type { [4 x i64] }
|
|
|
|
define %scalar @pr31719(ptr nocapture readonly %this, %scalar %arg.b) nounwind {
|
|
; CHECK-LABEL: pr31719:
|
|
; CHECK: # %bb.0: # %entry
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: addq (%rsi), %rdx
|
|
; CHECK-NEXT: adcq 8(%rsi), %rcx
|
|
; CHECK-NEXT: adcq 16(%rsi), %r8
|
|
; CHECK-NEXT: adcq 24(%rsi), %r9
|
|
; CHECK-NEXT: movq %rdx, (%rdi)
|
|
; CHECK-NEXT: movq %rcx, 8(%rdi)
|
|
; CHECK-NEXT: movq %r8, 16(%rdi)
|
|
; CHECK-NEXT: movq %r9, 24(%rdi)
|
|
; CHECK-NEXT: retq
|
|
entry:
|
|
%0 = extractvalue %scalar %arg.b, 0
|
|
%.elt = extractvalue [4 x i64] %0, 0
|
|
%.elt24 = extractvalue [4 x i64] %0, 1
|
|
%.elt26 = extractvalue [4 x i64] %0, 2
|
|
%.elt28 = extractvalue [4 x i64] %0, 3
|
|
%1 = load i64, ptr %this, align 8
|
|
%2 = zext i64 %1 to i128
|
|
%3 = zext i64 %.elt to i128
|
|
%4 = add nuw nsw i128 %2, %3
|
|
%5 = trunc i128 %4 to i64
|
|
%6 = lshr i128 %4, 64
|
|
%7 = getelementptr inbounds %scalar , ptr %this, i64 0, i32 0, i64 1
|
|
%8 = load i64, ptr %7, align 8
|
|
%9 = zext i64 %8 to i128
|
|
%10 = zext i64 %.elt24 to i128
|
|
%11 = add nuw nsw i128 %9, %10
|
|
%12 = add nuw nsw i128 %11, %6
|
|
%13 = trunc i128 %12 to i64
|
|
%14 = lshr i128 %12, 64
|
|
%15 = getelementptr inbounds %scalar , ptr %this, i64 0, i32 0, i64 2
|
|
%16 = load i64, ptr %15, align 8
|
|
%17 = zext i64 %16 to i128
|
|
%18 = zext i64 %.elt26 to i128
|
|
%19 = add nuw nsw i128 %17, %18
|
|
%20 = add nuw nsw i128 %19, %14
|
|
%21 = trunc i128 %20 to i64
|
|
%22 = lshr i128 %20, 64
|
|
%23 = getelementptr inbounds %scalar , ptr %this, i64 0, i32 0, i64 3
|
|
%24 = load i64, ptr %23, align 8
|
|
%25 = zext i64 %24 to i128
|
|
%26 = zext i64 %.elt28 to i128
|
|
%27 = add nuw nsw i128 %25, %26
|
|
%28 = add nuw nsw i128 %27, %22
|
|
%29 = trunc i128 %28 to i64
|
|
%30 = insertvalue [4 x i64] undef, i64 %5, 0
|
|
%31 = insertvalue [4 x i64] %30, i64 %13, 1
|
|
%32 = insertvalue [4 x i64] %31, i64 %21, 2
|
|
%33 = insertvalue [4 x i64] %32, i64 %29, 3
|
|
%34 = insertvalue %scalar undef, [4 x i64] %33, 0
|
|
ret %scalar %34
|
|
}
|
|
|
|
%accumulator= type { i64, i64, i32 }
|
|
|
|
define void @muladd(ptr nocapture %this, i64 %arg.a, i64 %arg.b) nounwind {
|
|
; CHECK-LABEL: muladd:
|
|
; CHECK: # %bb.0: # %entry
|
|
; CHECK-NEXT: movq %rdx, %rax
|
|
; CHECK-NEXT: mulq %rsi
|
|
; CHECK-NEXT: addq %rax, (%rdi)
|
|
; CHECK-NEXT: adcq %rdx, 8(%rdi)
|
|
; CHECK-NEXT: adcl $0, 16(%rdi)
|
|
; CHECK-NEXT: retq
|
|
entry:
|
|
%0 = zext i64 %arg.a to i128
|
|
%1 = zext i64 %arg.b to i128
|
|
%2 = mul nuw i128 %1, %0
|
|
%3 = load i64, ptr %this, align 8
|
|
%4 = zext i64 %3 to i128
|
|
%5 = add i128 %4, %2
|
|
%6 = trunc i128 %5 to i64
|
|
store i64 %6, ptr %this, align 8
|
|
%7 = lshr i128 %5, 64
|
|
%8 = getelementptr inbounds %accumulator, ptr %this, i64 0, i32 1
|
|
%9 = load i64, ptr %8, align 8
|
|
%10 = zext i64 %9 to i128
|
|
%11 = add nuw nsw i128 %7, %10
|
|
%12 = trunc i128 %11 to i64
|
|
store i64 %12, ptr %8, align 8
|
|
%13 = lshr i128 %11, 64
|
|
%14 = getelementptr inbounds %accumulator, ptr %this, i64 0, i32 2
|
|
%15 = load i32, ptr %14, align 4
|
|
%16 = zext i32 %15 to i128
|
|
%17 = add nuw nsw i128 %13, %16
|
|
%18 = trunc i128 %17 to i32
|
|
store i32 %18, ptr %14, align 4
|
|
ret void
|
|
}
|
|
|
|
define i64 @shiftadd(i64 %a, i64 %b, i64 %c, i64 %d) nounwind {
|
|
; CHECK-LABEL: shiftadd:
|
|
; CHECK: # %bb.0: # %entry
|
|
; CHECK-NEXT: movq %rdx, %rax
|
|
; CHECK-NEXT: addq %rsi, %rdi
|
|
; CHECK-NEXT: adcq %rcx, %rax
|
|
; CHECK-NEXT: retq
|
|
entry:
|
|
%0 = zext i64 %a to i128
|
|
%1 = zext i64 %b to i128
|
|
%2 = add i128 %0, %1
|
|
%3 = lshr i128 %2, 64
|
|
%4 = trunc i128 %3 to i64
|
|
%5 = add i64 %c, %d
|
|
%6 = add i64 %4, %5
|
|
ret i64 %6
|
|
}
|
|
|
|
%S = type { [4 x i64] }
|
|
|
|
define %S @readd(ptr nocapture readonly %this, %S %arg.b) nounwind {
|
|
; CHECK-LABEL: readd:
|
|
; CHECK: # %bb.0: # %entry
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: addq (%rsi), %rdx
|
|
; CHECK-NEXT: movq 8(%rsi), %rdi
|
|
; CHECK-NEXT: adcq $0, %rdi
|
|
; CHECK-NEXT: setb %r10b
|
|
; CHECK-NEXT: movzbl %r10b, %r10d
|
|
; CHECK-NEXT: addq %rcx, %rdi
|
|
; CHECK-NEXT: adcq 16(%rsi), %r10
|
|
; CHECK-NEXT: setb %cl
|
|
; CHECK-NEXT: movzbl %cl, %ecx
|
|
; CHECK-NEXT: addq %r8, %r10
|
|
; CHECK-NEXT: adcq 24(%rsi), %rcx
|
|
; CHECK-NEXT: addq %r9, %rcx
|
|
; CHECK-NEXT: movq %rdx, (%rax)
|
|
; CHECK-NEXT: movq %rdi, 8(%rax)
|
|
; CHECK-NEXT: movq %r10, 16(%rax)
|
|
; CHECK-NEXT: movq %rcx, 24(%rax)
|
|
; CHECK-NEXT: retq
|
|
entry:
|
|
%0 = extractvalue %S %arg.b, 0
|
|
%.elt6 = extractvalue [4 x i64] %0, 1
|
|
%.elt8 = extractvalue [4 x i64] %0, 2
|
|
%.elt10 = extractvalue [4 x i64] %0, 3
|
|
%.elt = extractvalue [4 x i64] %0, 0
|
|
%1 = load i64, ptr %this, align 8
|
|
%2 = zext i64 %1 to i128
|
|
%3 = zext i64 %.elt to i128
|
|
%4 = add nuw nsw i128 %2, %3
|
|
%5 = trunc i128 %4 to i64
|
|
%6 = lshr i128 %4, 64
|
|
%7 = getelementptr inbounds %S, ptr %this, i64 0, i32 0, i64 1
|
|
%8 = load i64, ptr %7, align 8
|
|
%9 = zext i64 %8 to i128
|
|
%10 = add nuw nsw i128 %6, %9
|
|
%11 = zext i64 %.elt6 to i128
|
|
%12 = add nuw nsw i128 %10, %11
|
|
%13 = trunc i128 %12 to i64
|
|
%14 = lshr i128 %12, 64
|
|
%15 = getelementptr inbounds %S, ptr %this, i64 0, i32 0, i64 2
|
|
%16 = load i64, ptr %15, align 8
|
|
%17 = zext i64 %16 to i128
|
|
%18 = add nuw nsw i128 %14, %17
|
|
%19 = zext i64 %.elt8 to i128
|
|
%20 = add nuw nsw i128 %18, %19
|
|
%21 = lshr i128 %20, 64
|
|
%22 = trunc i128 %20 to i64
|
|
%23 = getelementptr inbounds %S, ptr %this, i64 0,i32 0, i64 3
|
|
%24 = load i64, ptr %23, align 8
|
|
%25 = zext i64 %24 to i128
|
|
%26 = add nuw nsw i128 %21, %25
|
|
%27 = zext i64 %.elt10 to i128
|
|
%28 = add nuw nsw i128 %26, %27
|
|
%29 = trunc i128 %28 to i64
|
|
%30 = insertvalue [4 x i64] undef, i64 %5, 0
|
|
%31 = insertvalue [4 x i64] %30, i64 %13, 1
|
|
%32 = insertvalue [4 x i64] %31, i64 %22, 2
|
|
%33 = insertvalue [4 x i64] %32, i64 %29, 3
|
|
%34 = insertvalue %S undef, [4 x i64] %33, 0
|
|
ret %S %34
|
|
}
|
|
|
|
define i128 @addcarry1_not(i128 %n) nounwind {
|
|
; CHECK-LABEL: addcarry1_not:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: xorl %edx, %edx
|
|
; CHECK-NEXT: negq %rax
|
|
; CHECK-NEXT: sbbq %rsi, %rdx
|
|
; CHECK-NEXT: retq
|
|
%1 = xor i128 %n, -1
|
|
%2 = add i128 %1, 1
|
|
ret i128 %2
|
|
}
|
|
|
|
define { i128, i1 } @saddo_not_1(i128 %x) nounwind {
|
|
; CHECK-LABEL: saddo_not_1:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: xorl %edx, %edx
|
|
; CHECK-NEXT: negq %rax
|
|
; CHECK-NEXT: sbbq %rsi, %rdx
|
|
; CHECK-NEXT: seto %cl
|
|
; CHECK-NEXT: retq
|
|
%not = xor i128 %x, -1
|
|
%r = call { i128, i1 } @llvm.sadd.with.overflow.i128(i128 %not, i128 1)
|
|
ret { i128, i1 } %r
|
|
}
|
|
|
|
define { i128, i1 } @saddo_carry_not_1(i128 %x) nounwind {
|
|
; CHECK-LABEL: saddo_carry_not_1:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: negq %rax
|
|
; CHECK-NEXT: movl $1, %edx
|
|
; CHECK-NEXT: sbbq %rsi, %rdx
|
|
; CHECK-NEXT: seto %cl
|
|
; CHECK-NEXT: retq
|
|
%not = xor i128 %x, -1
|
|
%r = call { i128, i1 } @llvm.sadd.with.overflow.i128(i128 %not, i128 u0x10000000000000001)
|
|
ret { i128, i1 } %r
|
|
}
|
|
|
|
define i128 @addcarry_to_subcarry(i64 %a, i64 %b) nounwind {
|
|
; CHECK-LABEL: addcarry_to_subcarry:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: cmpq %rsi, %rdi
|
|
; CHECK-NEXT: notq %rsi
|
|
; CHECK-NEXT: setae %cl
|
|
; CHECK-NEXT: addb $-1, %cl
|
|
; CHECK-NEXT: adcq $0, %rax
|
|
; CHECK-NEXT: setb %cl
|
|
; CHECK-NEXT: movzbl %cl, %edx
|
|
; CHECK-NEXT: addq %rsi, %rax
|
|
; CHECK-NEXT: adcq $0, %rdx
|
|
; CHECK-NEXT: retq
|
|
%notb = xor i64 %b, -1
|
|
%notb128 = zext i64 %notb to i128
|
|
%a128 = zext i64 %a to i128
|
|
%sum1 = add i128 %a128, 1
|
|
%sub1 = add i128 %sum1, %notb128
|
|
%hi = lshr i128 %sub1, 64
|
|
%sum2 = add i128 %hi, %a128
|
|
%sub2 = add i128 %sum2, %notb128
|
|
ret i128 %sub2
|
|
}
|
|
|
|
; basic test for combineCarryDiamond()
|
|
define { i64, i64, i1 } @addcarry_2x64(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
|
|
; CHECK-LABEL: addcarry_2x64:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: addq %rdx, %rax
|
|
; CHECK-NEXT: adcq %rcx, %rsi
|
|
; CHECK-NEXT: setb %cl
|
|
; CHECK-NEXT: movq %rsi, %rdx
|
|
; CHECK-NEXT: retq
|
|
%t0 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x0, i64 %y0)
|
|
%s0 = extractvalue { i64, i1 } %t0, 0
|
|
%k0 = extractvalue { i64, i1 } %t0, 1
|
|
|
|
%t1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x1, i64 %y1)
|
|
%s1 = extractvalue { i64, i1 } %t1, 0
|
|
%k1 = extractvalue { i64, i1 } %t1, 1
|
|
|
|
%zk0 = zext i1 %k0 to i64
|
|
%t2 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %s1, i64 %zk0)
|
|
%s2 = extractvalue { i64, i1 } %t2, 0
|
|
%k2 = extractvalue { i64, i1 } %t2, 1
|
|
%k = or i1 %k1, %k2
|
|
|
|
%r0 = insertvalue { i64, i64, i1 } poison, i64 %s0, 0
|
|
%r1 = insertvalue { i64, i64, i1 } %r0, i64 %s2, 1
|
|
%r = insertvalue { i64, i64, i1 } %r1, i1 %k, 2
|
|
ret { i64, i64, i1 } %r
|
|
}
|
|
|
|
; basic test for combineCarryDiamond() with carries behind zext/and/trunc
|
|
define { i64, i64, i1 } @addcarry_hidden_2x64(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
|
|
; CHECK-LABEL: addcarry_hidden_2x64:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: addq %rdx, %rax
|
|
; CHECK-NEXT: adcq %rcx, %rsi
|
|
; CHECK-NEXT: setb %cl
|
|
; CHECK-NEXT: movq %rsi, %rdx
|
|
; CHECK-NEXT: retq
|
|
%t0 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x0, i64 %y0)
|
|
%s0 = extractvalue { i64, i1 } %t0, 0
|
|
%k0 = extractvalue { i64, i1 } %t0, 1
|
|
|
|
%t1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x1, i64 %y1)
|
|
%s1 = extractvalue { i64, i1 } %t1, 0
|
|
%k1 = extractvalue { i64, i1 } %t1, 1
|
|
%k1i8 = zext i1 %k1 to i8
|
|
%k1and = and i8 %k1i8, 1
|
|
%k1hidden = trunc i8 %k1and to i1
|
|
|
|
%zk0 = zext i1 %k0 to i64
|
|
%t2 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %s1, i64 %zk0)
|
|
%s2 = extractvalue { i64, i1 } %t2, 0
|
|
%k2 = extractvalue { i64, i1 } %t2, 1
|
|
|
|
%k = or i1 %k1hidden, %k2
|
|
|
|
%r0 = insertvalue { i64, i64, i1 } poison, i64 %s0, 0
|
|
%r1 = insertvalue { i64, i64, i1 } %r0, i64 %s2, 1
|
|
%r = insertvalue { i64, i64, i1 } %r1, i1 %k, 2
|
|
ret { i64, i64, i1 } %r
|
|
}
|
|
|
|
; basic test for combineCarryDiamond() with carries behind zext/and/trunc
|
|
define { i64, i64, i1 } @addcarry_hidden2_2x64(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
|
|
; CHECK-LABEL: addcarry_hidden2_2x64:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: addq %rdx, %rax
|
|
; CHECK-NEXT: adcq %rcx, %rsi
|
|
; CHECK-NEXT: setb %cl
|
|
; CHECK-NEXT: movq %rsi, %rdx
|
|
; CHECK-NEXT: retq
|
|
%t0 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x0, i64 %y0)
|
|
%s0 = extractvalue { i64, i1 } %t0, 0
|
|
%k0 = extractvalue { i64, i1 } %t0, 1
|
|
|
|
%t1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x1, i64 %y1)
|
|
%s1 = extractvalue { i64, i1 } %t1, 0
|
|
%k1 = extractvalue { i64, i1 } %t1, 1
|
|
|
|
%zk0 = zext i1 %k0 to i64
|
|
%t2 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %s1, i64 %zk0)
|
|
%s2 = extractvalue { i64, i1 } %t2, 0
|
|
%k2 = extractvalue { i64, i1 } %t2, 1
|
|
%k2i8 = zext i1 %k2 to i8
|
|
%k2and = and i8 %k2i8, 1
|
|
%k2hidden = trunc i8 %k2and to i1
|
|
|
|
%k = or i1 %k1, %k2hidden
|
|
|
|
%r0 = insertvalue { i64, i64, i1 } poison, i64 %s0, 0
|
|
%r1 = insertvalue { i64, i64, i1 } %r0, i64 %s2, 1
|
|
%r = insertvalue { i64, i64, i1 } %r1, i1 %k, 2
|
|
ret { i64, i64, i1 } %r
|
|
}
|
|
|
|
; basic test for combineCarryDiamond() with or operands reversed
|
|
define { i64, i64, i1 } @addcarry_2x64_or_reversed(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
|
|
; CHECK-LABEL: addcarry_2x64_or_reversed:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: addq %rdx, %rax
|
|
; CHECK-NEXT: adcq %rcx, %rsi
|
|
; CHECK-NEXT: setb %cl
|
|
; CHECK-NEXT: movq %rsi, %rdx
|
|
; CHECK-NEXT: retq
|
|
%t0 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x0, i64 %y0)
|
|
%s0 = extractvalue { i64, i1 } %t0, 0
|
|
%k0 = extractvalue { i64, i1 } %t0, 1
|
|
|
|
%t1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x1, i64 %y1)
|
|
%s1 = extractvalue { i64, i1 } %t1, 0
|
|
%k1 = extractvalue { i64, i1 } %t1, 1
|
|
|
|
%zk0 = zext i1 %k0 to i64
|
|
%t2 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %zk0, i64 %s1) ; reversed
|
|
%s2 = extractvalue { i64, i1 } %t2, 0
|
|
%k2 = extractvalue { i64, i1 } %t2, 1
|
|
%k = or i1 %k2, %k1 ; reverse natural order of operands
|
|
|
|
%r0 = insertvalue { i64, i64, i1 } poison, i64 %s0, 0
|
|
%r1 = insertvalue { i64, i64, i1 } %r0, i64 %s2, 1
|
|
%r = insertvalue { i64, i64, i1 } %r1, i1 %k, 2
|
|
ret { i64, i64, i1 } %r
|
|
}
|
|
|
|
; basic test for combineCarryDiamond() with xor operands reversed
|
|
define { i64, i64, i1 } @addcarry_2x64_xor_reversed(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
|
|
; CHECK-LABEL: addcarry_2x64_xor_reversed:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: addq %rdx, %rax
|
|
; CHECK-NEXT: adcq %rcx, %rsi
|
|
; CHECK-NEXT: setb %cl
|
|
; CHECK-NEXT: movq %rsi, %rdx
|
|
; CHECK-NEXT: retq
|
|
%t0 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x0, i64 %y0)
|
|
%s0 = extractvalue { i64, i1 } %t0, 0
|
|
%k0 = extractvalue { i64, i1 } %t0, 1
|
|
|
|
%t1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x1, i64 %y1)
|
|
%s1 = extractvalue { i64, i1 } %t1, 0
|
|
%k1 = extractvalue { i64, i1 } %t1, 1
|
|
|
|
%zk0 = zext i1 %k0 to i64
|
|
%t2 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %s1, i64 %zk0)
|
|
%s2 = extractvalue { i64, i1 } %t2, 0
|
|
%k2 = extractvalue { i64, i1 } %t2, 1
|
|
%k = xor i1 %k2, %k1 ; reverse natural order of operands
|
|
|
|
%r0 = insertvalue { i64, i64, i1 } poison, i64 %s0, 0
|
|
%r1 = insertvalue { i64, i64, i1 } %r0, i64 %s2, 1
|
|
%r = insertvalue { i64, i64, i1 } %r1, i1 %k, 2
|
|
ret { i64, i64, i1 } %r
|
|
}
|
|
|
|
; basic test for combineCarryDiamond() with and operands reversed
|
|
define { i64, i64, i1 } @addcarry_2x64_and_reversed(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
|
|
; CHECK-LABEL: addcarry_2x64_and_reversed:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: addq %rdx, %rax
|
|
; CHECK-NEXT: adcq %rcx, %rsi
|
|
; CHECK-NEXT: movq %rsi, %rdx
|
|
; CHECK-NEXT: xorl %ecx, %ecx
|
|
; CHECK-NEXT: retq
|
|
%t0 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x0, i64 %y0)
|
|
%s0 = extractvalue { i64, i1 } %t0, 0
|
|
%k0 = extractvalue { i64, i1 } %t0, 1
|
|
|
|
%t1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x1, i64 %y1)
|
|
%s1 = extractvalue { i64, i1 } %t1, 0
|
|
%k1 = extractvalue { i64, i1 } %t1, 1
|
|
|
|
%zk0 = zext i1 %k0 to i64
|
|
%t2 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %s1, i64 %zk0)
|
|
%s2 = extractvalue { i64, i1 } %t2, 0
|
|
%k2 = extractvalue { i64, i1 } %t2, 1
|
|
%k = and i1 %k2, %k1 ; reverse natural order of operands
|
|
|
|
%r0 = insertvalue { i64, i64, i1 } poison, i64 %s0, 0
|
|
%r1 = insertvalue { i64, i64, i1 } %r0, i64 %s2, 1
|
|
%r = insertvalue { i64, i64, i1 } %r1, i1 %k, 2
|
|
ret { i64, i64, i1 } %r
|
|
}
|
|
|
|
; basic test for combineCarryDiamond() with add operands reversed
|
|
define { i64, i64, i1 } @addcarry_2x64_add_reversed(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
|
|
; CHECK-LABEL: addcarry_2x64_add_reversed:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: addq %rdx, %rax
|
|
; CHECK-NEXT: adcq %rcx, %rsi
|
|
; CHECK-NEXT: setb %cl
|
|
; CHECK-NEXT: movq %rsi, %rdx
|
|
; CHECK-NEXT: retq
|
|
%t0 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x0, i64 %y0)
|
|
%s0 = extractvalue { i64, i1 } %t0, 0
|
|
%k0 = extractvalue { i64, i1 } %t0, 1
|
|
|
|
%t1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x1, i64 %y1)
|
|
%s1 = extractvalue { i64, i1 } %t1, 0
|
|
%k1 = extractvalue { i64, i1 } %t1, 1
|
|
|
|
%zk0 = zext i1 %k0 to i64
|
|
%t2 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %s1, i64 %zk0)
|
|
%s2 = extractvalue { i64, i1 } %t2, 0
|
|
%k2 = extractvalue { i64, i1 } %t2, 1
|
|
%k = add i1 %k2, %k1 ; reverse natural order of operands
|
|
|
|
%r0 = insertvalue { i64, i64, i1 } poison, i64 %s0, 0
|
|
%r1 = insertvalue { i64, i64, i1 } %r0, i64 %s2, 1
|
|
%r = insertvalue { i64, i64, i1 } %r1, i1 %k, 2
|
|
ret { i64, i64, i1 } %r
|
|
}
|
|
|
|
; Here %carryin is considered as valid carry flag for combining into ADDCARRY
|
|
; although %carryin does not come from any carry-producing instruction.
|
|
define { i64, i1 } @addcarry_fake_carry(i64 %a, i64 %b, i1 %carryin) nounwind {
|
|
; CHECK-LABEL: addcarry_fake_carry:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: btl $0, %edx
|
|
; CHECK-NEXT: adcq %rsi, %rax
|
|
; CHECK-NEXT: setb %dl
|
|
; CHECK-NEXT: retq
|
|
%t1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %a, i64 %b)
|
|
%partial = extractvalue { i64, i1 } %t1, 0
|
|
%k1 = extractvalue { i64, i1 } %t1, 1
|
|
|
|
%zcarryin = zext i1 %carryin to i64
|
|
%sum = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %partial, i64 %zcarryin)
|
|
%k2 = extractvalue { i64, i1 } %sum, 1
|
|
|
|
%carryout = or i1 %k1, %k2
|
|
|
|
%ret = insertvalue { i64, i1 } %sum, i1 %carryout, 1
|
|
ret { i64, i1 } %ret
|
|
}
|
|
|
|
; negative test: %carryin does not look like carry
|
|
define { i64, i1 } @addcarry_carry_not_zext(i64 %a, i64 %b, i64 %carryin) nounwind {
|
|
; CHECK-LABEL: addcarry_carry_not_zext:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: addq %rsi, %rax
|
|
; CHECK-NEXT: setb %cl
|
|
; CHECK-NEXT: addq %rdx, %rax
|
|
; CHECK-NEXT: setb %dl
|
|
; CHECK-NEXT: orb %cl, %dl
|
|
; CHECK-NEXT: retq
|
|
%t1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %a, i64 %b)
|
|
%partial = extractvalue { i64, i1 } %t1, 0
|
|
%k1 = extractvalue { i64, i1 } %t1, 1
|
|
|
|
%sum = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %partial, i64 %carryin)
|
|
%k2 = extractvalue { i64, i1 } %sum, 1
|
|
|
|
%carryout = or i1 %k1, %k2
|
|
|
|
%ret = insertvalue { i64, i1 } %sum, i1 %carryout, 1
|
|
ret { i64, i1 } %ret
|
|
}
|
|
|
|
; negative test: %carryin does not look like carry
|
|
define { i64, i1 } @addcarry_carry_not_i1(i64 %a, i64 %b, i8 %carryin) nounwind {
|
|
; CHECK-LABEL: addcarry_carry_not_i1:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: addq %rsi, %rdi
|
|
; CHECK-NEXT: setb %cl
|
|
; CHECK-NEXT: movzbl %dl, %eax
|
|
; CHECK-NEXT: addq %rdi, %rax
|
|
; CHECK-NEXT: setb %dl
|
|
; CHECK-NEXT: orb %cl, %dl
|
|
; CHECK-NEXT: retq
|
|
%t1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %a, i64 %b)
|
|
%partial = extractvalue { i64, i1 } %t1, 0
|
|
%k1 = extractvalue { i64, i1 } %t1, 1
|
|
|
|
%zcarryin = zext i8 %carryin to i64
|
|
%sum = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %partial, i64 %zcarryin)
|
|
%k2 = extractvalue { i64, i1 } %sum, 1
|
|
|
|
%carryout = or i1 %k1, %k2
|
|
|
|
%ret = insertvalue { i64, i1 } %sum, i1 %carryout, 1
|
|
ret { i64, i1 } %ret
|
|
}
|
|
|
|
; Check that we can reconstruct a carry if it is masked.
|
|
define { i64, i1 } @addcarry_carry_and_1(i64 %a, i64 %b, i64 %carryin) nounwind {
|
|
; CHECK-LABEL: addcarry_carry_and_1:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: btl $0, %edx
|
|
; CHECK-NEXT: adcq %rsi, %rax
|
|
; CHECK-NEXT: setb %dl
|
|
; CHECK-NEXT: retq
|
|
%t1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %a, i64 %b)
|
|
%partial = extractvalue { i64, i1 } %t1, 0
|
|
%k1 = extractvalue { i64, i1 } %t1, 1
|
|
|
|
%mcarryin = and i64 %carryin, 1
|
|
%sum = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %partial, i64 %mcarryin)
|
|
%k2 = extractvalue { i64, i1 } %sum, 1
|
|
|
|
%carryout = or i1 %k1, %k2
|
|
|
|
%ret = insertvalue { i64, i1 } %sum, i1 %carryout, 1
|
|
ret { i64, i1 } %ret
|
|
}
|
|
|
|
; negative test for combineCarryDiamond(): uaddo mixed with usubo
|
|
define { i64, i64, i1 } @addcarry_mixed_2x64(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
|
|
; CHECK-LABEL: addcarry_mixed_2x64:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: addq %rcx, %rsi
|
|
; CHECK-NEXT: setb %dil
|
|
; CHECK-NEXT: addq %rdx, %rax
|
|
; CHECK-NEXT: sbbq $0, %rsi
|
|
; CHECK-NEXT: setb %cl
|
|
; CHECK-NEXT: orb %dil, %cl
|
|
; CHECK-NEXT: movq %rsi, %rdx
|
|
; CHECK-NEXT: retq
|
|
%t0 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x0, i64 %y0)
|
|
%s0 = extractvalue { i64, i1 } %t0, 0
|
|
%k0 = extractvalue { i64, i1 } %t0, 1
|
|
|
|
%t1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x1, i64 %y1)
|
|
%s1 = extractvalue { i64, i1 } %t1, 0
|
|
%k1 = extractvalue { i64, i1 } %t1, 1
|
|
|
|
%zk0 = zext i1 %k0 to i64
|
|
%t2 = call { i64, i1 } @llvm.usub.with.overflow.i64(i64 %s1, i64 %zk0)
|
|
%s2 = extractvalue { i64, i1 } %t2, 0
|
|
%k2 = extractvalue { i64, i1 } %t2, 1
|
|
%k = or i1 %k1, %k2
|
|
|
|
%r0 = insertvalue { i64, i64, i1 } poison, i64 %s0, 0
|
|
%r1 = insertvalue { i64, i64, i1 } %r0, i64 %s2, 1
|
|
%r = insertvalue { i64, i64, i1 } %r1, i1 %k, 2
|
|
ret { i64, i64, i1 } %r
|
|
}
|
|
|
|
%struct.U320 = type { [5 x i64] }
|
|
|
|
define i32 @add_U320_without_i128_add(ptr nocapture dereferenceable(40) %0, i64 %1, i64 %2, i64 %3, i64 %4, i64 %5) nounwind {
|
|
; CHECK-LABEL: add_U320_without_i128_add:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq 16(%rdi), %rax
|
|
; CHECK-NEXT: movq 24(%rdi), %r10
|
|
; CHECK-NEXT: movq 32(%rdi), %r11
|
|
; CHECK-NEXT: addq %rsi, (%rdi)
|
|
; CHECK-NEXT: adcq %rdx, 8(%rdi)
|
|
; CHECK-NEXT: movq %rax, %rdx
|
|
; CHECK-NEXT: adcq %rcx, %rdx
|
|
; CHECK-NEXT: addq %rcx, %rax
|
|
; CHECK-NEXT: movq %r10, %rcx
|
|
; CHECK-NEXT: adcq %r8, %rcx
|
|
; CHECK-NEXT: cmpq %rax, %rdx
|
|
; CHECK-NEXT: adcq $0, %rcx
|
|
; CHECK-NEXT: leaq (%r11,%r9), %rsi
|
|
; CHECK-NEXT: addq %r8, %r10
|
|
; CHECK-NEXT: movq %r11, %r8
|
|
; CHECK-NEXT: adcq %r9, %r8
|
|
; CHECK-NEXT: cmpq %r10, %rcx
|
|
; CHECK-NEXT: adcq $0, %r8
|
|
; CHECK-NEXT: xorl %eax, %eax
|
|
; CHECK-NEXT: cmpq %rsi, %r8
|
|
; CHECK-NEXT: setb %al
|
|
; CHECK-NEXT: addq %r9, %r11
|
|
; CHECK-NEXT: movq %rdx, 16(%rdi)
|
|
; CHECK-NEXT: movq %rcx, 24(%rdi)
|
|
; CHECK-NEXT: movq %r8, 32(%rdi)
|
|
; CHECK-NEXT: adcl $0, %eax
|
|
; CHECK-NEXT: retq
|
|
%7 = load i64, ptr %0, align 8
|
|
%8 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 1
|
|
%9 = load i64, ptr %8, align 8
|
|
%10 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 2
|
|
%11 = load i64, ptr %10, align 8
|
|
%12 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 3
|
|
%13 = load i64, ptr %12, align 8
|
|
%14 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 4
|
|
%15 = load i64, ptr %14, align 8
|
|
%16 = add i64 %7, %1
|
|
%17 = add i64 %9, %2
|
|
%18 = icmp ult i64 %16, %1
|
|
%19 = zext i1 %18 to i64
|
|
%20 = add i64 %17, %19
|
|
%21 = add i64 %11, %3
|
|
%22 = icmp ult i64 %17, %9
|
|
%23 = zext i1 %22 to i64
|
|
%24 = icmp ult i64 %20, %17
|
|
%25 = zext i1 %24 to i64
|
|
%26 = add i64 %21, %23
|
|
%27 = add i64 %26, %25
|
|
%28 = add i64 %13, %4
|
|
%29 = icmp ult i64 %21, %11
|
|
%30 = zext i1 %29 to i64
|
|
%31 = icmp ult i64 %27, %21
|
|
%32 = zext i1 %31 to i64
|
|
%33 = add i64 %28, %30
|
|
%34 = add i64 %33, %32
|
|
%35 = add i64 %15, %5
|
|
%36 = icmp ult i64 %28, %13
|
|
%37 = zext i1 %36 to i64
|
|
%38 = icmp ult i64 %34, %28
|
|
%39 = zext i1 %38 to i64
|
|
%40 = add i64 %35, %37
|
|
%41 = add i64 %40, %39
|
|
store i64 %16, ptr %0, align 8
|
|
store i64 %20, ptr %8, align 8
|
|
store i64 %27, ptr %10, align 8
|
|
store i64 %34, ptr %12, align 8
|
|
store i64 %41, ptr %14, align 8
|
|
%42 = icmp ult i64 %35, %15
|
|
%43 = zext i1 %42 to i32
|
|
%44 = icmp ult i64 %41, %35
|
|
%45 = zext i1 %44 to i32
|
|
%46 = add nuw nsw i32 %45, %43
|
|
ret i32 %46
|
|
}
|
|
|
|
define i32 @add_U320_without_i128_or(ptr nocapture dereferenceable(40) %0, i64 %1, i64 %2, i64 %3, i64 %4, i64 %5) nounwind {
|
|
; CHECK-LABEL: add_U320_without_i128_or:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: addq %rsi, (%rdi)
|
|
; CHECK-NEXT: adcq %rdx, 8(%rdi)
|
|
; CHECK-NEXT: adcq %rcx, 16(%rdi)
|
|
; CHECK-NEXT: adcq %r8, 24(%rdi)
|
|
; CHECK-NEXT: adcq %r9, 32(%rdi)
|
|
; CHECK-NEXT: setb %al
|
|
; CHECK-NEXT: movzbl %al, %eax
|
|
; CHECK-NEXT: retq
|
|
%7 = load i64, ptr %0, align 8
|
|
%8 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 1
|
|
%9 = load i64, ptr %8, align 8
|
|
%10 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 2
|
|
%11 = load i64, ptr %10, align 8
|
|
%12 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 3
|
|
%13 = load i64, ptr %12, align 8
|
|
%14 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 4
|
|
%15 = load i64, ptr %14, align 8
|
|
%16 = add i64 %7, %1
|
|
%17 = add i64 %9, %2
|
|
%18 = icmp ult i64 %16, %1
|
|
%19 = zext i1 %18 to i64
|
|
%20 = add i64 %17, %19
|
|
%21 = add i64 %11, %3
|
|
%22 = icmp ult i64 %17, %9
|
|
%23 = icmp ult i64 %20, %17
|
|
%24 = or i1 %22, %23
|
|
%25 = zext i1 %24 to i64
|
|
%26 = add i64 %21, %25
|
|
%27 = add i64 %13, %4
|
|
%28 = icmp ult i64 %21, %11
|
|
%29 = icmp ult i64 %26, %21
|
|
%30 = or i1 %28, %29
|
|
%31 = zext i1 %30 to i64
|
|
%32 = add i64 %27, %31
|
|
%33 = add i64 %15, %5
|
|
%34 = icmp ult i64 %27, %13
|
|
%35 = icmp ult i64 %32, %27
|
|
%36 = or i1 %34, %35
|
|
%37 = zext i1 %36 to i64
|
|
%38 = add i64 %33, %37
|
|
store i64 %16, ptr %0, align 8
|
|
store i64 %20, ptr %8, align 8
|
|
store i64 %26, ptr %10, align 8
|
|
store i64 %32, ptr %12, align 8
|
|
store i64 %38, ptr %14, align 8
|
|
%39 = icmp ult i64 %33, %15
|
|
%40 = icmp ult i64 %38, %33
|
|
%41 = or i1 %39, %40
|
|
%42 = zext i1 %41 to i32
|
|
ret i32 %42
|
|
}
|
|
|
|
define i32 @add_U320_without_i128_xor(ptr nocapture dereferenceable(40) %0, i64 %1, i64 %2, i64 %3, i64 %4, i64 %5) nounwind {
|
|
; CHECK-LABEL: add_U320_without_i128_xor:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: addq %rsi, (%rdi)
|
|
; CHECK-NEXT: adcq %rdx, 8(%rdi)
|
|
; CHECK-NEXT: adcq %rcx, 16(%rdi)
|
|
; CHECK-NEXT: adcq %r8, 24(%rdi)
|
|
; CHECK-NEXT: adcq %r9, 32(%rdi)
|
|
; CHECK-NEXT: setb %al
|
|
; CHECK-NEXT: movzbl %al, %eax
|
|
; CHECK-NEXT: retq
|
|
%7 = load i64, ptr %0, align 8
|
|
%8 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 1
|
|
%9 = load i64, ptr %8, align 8
|
|
%10 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 2
|
|
%11 = load i64, ptr %10, align 8
|
|
%12 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 3
|
|
%13 = load i64, ptr %12, align 8
|
|
%14 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 4
|
|
%15 = load i64, ptr %14, align 8
|
|
%16 = add i64 %7, %1
|
|
%17 = add i64 %9, %2
|
|
%18 = icmp ult i64 %16, %1
|
|
%19 = zext i1 %18 to i64
|
|
%20 = add i64 %17, %19
|
|
%21 = add i64 %11, %3
|
|
%22 = icmp ult i64 %17, %9
|
|
%23 = icmp ult i64 %20, %17
|
|
%24 = xor i1 %22, %23
|
|
%25 = zext i1 %24 to i64
|
|
%26 = add i64 %21, %25
|
|
%27 = add i64 %13, %4
|
|
%28 = icmp ult i64 %21, %11
|
|
%29 = icmp ult i64 %26, %21
|
|
%30 = xor i1 %28, %29
|
|
%31 = zext i1 %30 to i64
|
|
%32 = add i64 %27, %31
|
|
%33 = add i64 %15, %5
|
|
%34 = icmp ult i64 %27, %13
|
|
%35 = icmp ult i64 %32, %27
|
|
%36 = xor i1 %34, %35
|
|
%37 = zext i1 %36 to i64
|
|
%38 = add i64 %33, %37
|
|
store i64 %16, ptr %0, align 8
|
|
store i64 %20, ptr %8, align 8
|
|
store i64 %26, ptr %10, align 8
|
|
store i64 %32, ptr %12, align 8
|
|
store i64 %38, ptr %14, align 8
|
|
%39 = icmp ult i64 %33, %15
|
|
%40 = icmp ult i64 %38, %33
|
|
%41 = xor i1 %39, %40
|
|
%42 = zext i1 %41 to i32
|
|
ret i32 %42
|
|
}
|
|
|
|
; Either the primary addition can overflow or the addition of the carry, but
|
|
; they cannot both overflow.
|
|
define i32 @bogus_add_U320_without_i128_and(ptr nocapture dereferenceable(40) %0, i64 %1, i64 %2, i64 %3, i64 %4, i64 %5) nounwind {
|
|
; CHECK-LABEL: bogus_add_U320_without_i128_and:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: addq %rsi, (%rdi)
|
|
; CHECK-NEXT: adcq %rdx, 8(%rdi)
|
|
; CHECK-NEXT: addq %rcx, 16(%rdi)
|
|
; CHECK-NEXT: addq %r8, 24(%rdi)
|
|
; CHECK-NEXT: addq %r9, 32(%rdi)
|
|
; CHECK-NEXT: xorl %eax, %eax
|
|
; CHECK-NEXT: retq
|
|
%7 = load i64, ptr %0, align 8
|
|
%8 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 1
|
|
%9 = load i64, ptr %8, align 8
|
|
%10 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 2
|
|
%11 = load i64, ptr %10, align 8
|
|
%12 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 3
|
|
%13 = load i64, ptr %12, align 8
|
|
%14 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 4
|
|
%15 = load i64, ptr %14, align 8
|
|
%16 = add i64 %7, %1
|
|
%17 = add i64 %9, %2
|
|
%18 = icmp ult i64 %16, %1
|
|
%19 = zext i1 %18 to i64
|
|
%20 = add i64 %17, %19
|
|
%21 = add i64 %11, %3
|
|
%22 = icmp ult i64 %17, %9
|
|
%23 = icmp ult i64 %20, %17
|
|
%24 = and i1 %22, %23
|
|
%25 = zext i1 %24 to i64
|
|
%26 = add i64 %21, %25
|
|
%27 = add i64 %13, %4
|
|
%28 = icmp ult i64 %21, %11
|
|
%29 = icmp ult i64 %26, %21
|
|
%30 = and i1 %28, %29
|
|
%31 = zext i1 %30 to i64
|
|
%32 = add i64 %27, %31
|
|
%33 = add i64 %15, %5
|
|
%34 = icmp ult i64 %27, %13
|
|
%35 = icmp ult i64 %32, %27
|
|
%36 = and i1 %34, %35
|
|
%37 = zext i1 %36 to i64
|
|
%38 = add i64 %33, %37
|
|
store i64 %16, ptr %0, align 8
|
|
store i64 %20, ptr %8, align 8
|
|
store i64 %26, ptr %10, align 8
|
|
store i64 %32, ptr %12, align 8
|
|
store i64 %38, ptr %14, align 8
|
|
%39 = icmp ult i64 %33, %15
|
|
%40 = icmp ult i64 %38, %33
|
|
%41 = and i1 %39, %40
|
|
%42 = zext i1 %41 to i32
|
|
ret i32 %42
|
|
}
|
|
|
|
define void @add_U320_without_i128_or_no_ret(ptr nocapture dereferenceable(40) %0, i64 %1, i64 %2, i64 %3, i64 %4, i64 %5) nounwind {
|
|
; CHECK-LABEL: add_U320_without_i128_or_no_ret:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: addq %rsi, (%rdi)
|
|
; CHECK-NEXT: adcq %rdx, 8(%rdi)
|
|
; CHECK-NEXT: adcq %rcx, 16(%rdi)
|
|
; CHECK-NEXT: adcq %r8, 24(%rdi)
|
|
; CHECK-NEXT: adcq %r9, 32(%rdi)
|
|
; CHECK-NEXT: retq
|
|
%7 = load i64, ptr %0, align 8
|
|
%8 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 1
|
|
%9 = load i64, ptr %8, align 8
|
|
%10 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 2
|
|
%11 = load i64, ptr %10, align 8
|
|
%12 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 3
|
|
%13 = load i64, ptr %12, align 8
|
|
%14 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 4
|
|
%15 = load i64, ptr %14, align 8
|
|
%16 = add i64 %7, %1
|
|
%17 = add i64 %9, %2
|
|
%18 = icmp ult i64 %16, %1
|
|
%19 = zext i1 %18 to i64
|
|
%20 = add i64 %17, %19
|
|
%21 = add i64 %11, %3
|
|
%22 = icmp ult i64 %17, %9
|
|
%23 = icmp ult i64 %20, %17
|
|
%24 = or i1 %22, %23
|
|
%25 = zext i1 %24 to i64
|
|
%26 = add i64 %21, %25
|
|
%27 = add i64 %13, %4
|
|
%28 = icmp ult i64 %21, %11
|
|
%29 = icmp ult i64 %26, %21
|
|
%30 = or i1 %28, %29
|
|
%31 = zext i1 %30 to i64
|
|
%32 = add i64 %27, %31
|
|
%33 = add i64 %15, %5
|
|
%34 = icmp ult i64 %27, %13
|
|
%35 = icmp ult i64 %32, %27
|
|
%36 = or i1 %34, %35
|
|
%37 = zext i1 %36 to i64
|
|
%38 = add i64 %33, %37
|
|
store i64 %16, ptr %0, align 8
|
|
store i64 %20, ptr %8, align 8
|
|
store i64 %26, ptr %10, align 8
|
|
store i64 %32, ptr %12, align 8
|
|
store i64 %38, ptr %14, align 8
|
|
ret void
|
|
}
|
|
|
|
define i32 @add_U320_uaddo(ptr nocapture dereferenceable(40) %0, i64 %1, i64 %2, i64 %3, i64 %4, i64 %5) nounwind {
|
|
; CHECK-LABEL: add_U320_uaddo:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: addq %rsi, (%rdi)
|
|
; CHECK-NEXT: adcq %rdx, 8(%rdi)
|
|
; CHECK-NEXT: adcq %rcx, 16(%rdi)
|
|
; CHECK-NEXT: adcq %r8, 24(%rdi)
|
|
; CHECK-NEXT: adcq %r9, 32(%rdi)
|
|
; CHECK-NEXT: setb %al
|
|
; CHECK-NEXT: movzbl %al, %eax
|
|
; CHECK-NEXT: retq
|
|
%7 = load i64, ptr %0, align 8
|
|
%8 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 1
|
|
%9 = load i64, ptr %8, align 8
|
|
%10 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 2
|
|
%11 = load i64, ptr %10, align 8
|
|
%12 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 3
|
|
%13 = load i64, ptr %12, align 8
|
|
%14 = getelementptr inbounds %struct.U320, ptr %0, i64 0, i32 0, i64 4
|
|
%15 = load i64, ptr %14, align 8
|
|
%16 = tail call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %7, i64 %1)
|
|
%17 = extractvalue { i64, i1 } %16, 1
|
|
%18 = extractvalue { i64, i1 } %16, 0
|
|
%19 = zext i1 %17 to i64
|
|
%20 = tail call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %9, i64 %2)
|
|
%21 = extractvalue { i64, i1 } %20, 1
|
|
%22 = extractvalue { i64, i1 } %20, 0
|
|
%23 = tail call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %22, i64 %19)
|
|
%24 = extractvalue { i64, i1 } %23, 1
|
|
%25 = extractvalue { i64, i1 } %23, 0
|
|
%26 = or i1 %21, %24
|
|
%27 = zext i1 %26 to i64
|
|
%28 = tail call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %11, i64 %3)
|
|
%29 = extractvalue { i64, i1 } %28, 1
|
|
%30 = extractvalue { i64, i1 } %28, 0
|
|
%31 = tail call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %30, i64 %27)
|
|
%32 = extractvalue { i64, i1 } %31, 1
|
|
%33 = extractvalue { i64, i1 } %31, 0
|
|
%34 = or i1 %29, %32
|
|
%35 = zext i1 %34 to i64
|
|
%36 = tail call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %13, i64 %4)
|
|
%37 = extractvalue { i64, i1 } %36, 1
|
|
%38 = extractvalue { i64, i1 } %36, 0
|
|
%39 = tail call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %38, i64 %35)
|
|
%40 = extractvalue { i64, i1 } %39, 1
|
|
%41 = extractvalue { i64, i1 } %39, 0
|
|
%42 = or i1 %37, %40
|
|
%43 = zext i1 %42 to i64
|
|
%44 = tail call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %15, i64 %5)
|
|
%45 = extractvalue { i64, i1 } %44, 1
|
|
%46 = extractvalue { i64, i1 } %44, 0
|
|
%47 = tail call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %46, i64 %43)
|
|
%48 = extractvalue { i64, i1 } %47, 1
|
|
%49 = extractvalue { i64, i1 } %47, 0
|
|
%50 = or i1 %45, %48
|
|
store i64 %18, ptr %0, align 8
|
|
store i64 %25, ptr %8, align 8
|
|
store i64 %33, ptr %10, align 8
|
|
store i64 %41, ptr %12, align 8
|
|
store i64 %49, ptr %14, align 8
|
|
%51 = zext i1 %50 to i32
|
|
ret i32 %51
|
|
}
|
|
|
|
%struct.U192 = type { [3 x i64] }
|
|
|
|
define void @PR39464(ptr noalias nocapture sret(%struct.U192) %0, ptr nocapture readonly dereferenceable(24) %1, ptr nocapture readonly dereferenceable(24) %2) nounwind {
|
|
; CHECK-LABEL: PR39464:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: movq (%rsi), %rcx
|
|
; CHECK-NEXT: addq (%rdx), %rcx
|
|
; CHECK-NEXT: movq %rcx, (%rdi)
|
|
; CHECK-NEXT: movq 8(%rsi), %rcx
|
|
; CHECK-NEXT: adcq 8(%rdx), %rcx
|
|
; CHECK-NEXT: movq %rcx, 8(%rdi)
|
|
; CHECK-NEXT: movq 16(%rsi), %rcx
|
|
; CHECK-NEXT: adcq 16(%rdx), %rcx
|
|
; CHECK-NEXT: movq %rcx, 16(%rdi)
|
|
; CHECK-NEXT: retq
|
|
%4 = load i64, ptr %1, align 8
|
|
%5 = load i64, ptr %2, align 8
|
|
%6 = tail call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %4, i64 %5)
|
|
%7 = extractvalue { i64, i1 } %6, 1
|
|
%8 = extractvalue { i64, i1 } %6, 0
|
|
%9 = zext i1 %7 to i64
|
|
store i64 %8, ptr %0, align 8
|
|
%10 = getelementptr inbounds %struct.U192, ptr %1, i64 0, i32 0, i64 1
|
|
%11 = load i64, ptr %10, align 8
|
|
%12 = getelementptr inbounds %struct.U192, ptr %2, i64 0, i32 0, i64 1
|
|
%13 = load i64, ptr %12, align 8
|
|
%14 = tail call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %11, i64 %13)
|
|
%15 = extractvalue { i64, i1 } %14, 1
|
|
%16 = extractvalue { i64, i1 } %14, 0
|
|
%17 = tail call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %16, i64 %9)
|
|
%18 = extractvalue { i64, i1 } %17, 1
|
|
%19 = extractvalue { i64, i1 } %17, 0
|
|
%20 = or i1 %15, %18
|
|
%21 = zext i1 %20 to i64
|
|
%22 = getelementptr inbounds %struct.U192, ptr %0, i64 0, i32 0, i64 1
|
|
store i64 %19, ptr %22, align 8
|
|
%23 = getelementptr inbounds %struct.U192, ptr %1, i64 0, i32 0, i64 2
|
|
%24 = load i64, ptr %23, align 8
|
|
%25 = getelementptr inbounds %struct.U192, ptr %2, i64 0, i32 0, i64 2
|
|
%26 = load i64, ptr %25, align 8
|
|
%27 = add i64 %24, %26
|
|
%28 = add i64 %27, %21
|
|
%29 = getelementptr inbounds %struct.U192, ptr %0, i64 0, i32 0, i64 2
|
|
store i64 %28, ptr %29, align 8
|
|
ret void
|
|
}
|
|
|
|
|
|
%uint128 = type { i64, i64 }
|
|
|
|
define zeroext i1 @uaddo_U128_without_i128_or(i64 %0, i64 %1, i64 %2, i64 %3, ptr nocapture %4) nounwind {
|
|
; CHECK-LABEL: uaddo_U128_without_i128_or:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: addq %rdx, %rdi
|
|
; CHECK-NEXT: adcq %rcx, %rsi
|
|
; CHECK-NEXT: setb %al
|
|
; CHECK-NEXT: movq %rsi, (%r8)
|
|
; CHECK-NEXT: movq %rdi, 8(%r8)
|
|
; CHECK-NEXT: retq
|
|
%6 = add i64 %2, %0
|
|
%7 = icmp ult i64 %6, %0
|
|
%8 = add i64 %3, %1
|
|
%9 = icmp ult i64 %8, %1
|
|
%10 = zext i1 %7 to i64
|
|
%11 = add i64 %8, %10
|
|
%12 = icmp ult i64 %11, %8
|
|
%13 = or i1 %9, %12
|
|
store i64 %11, ptr %4, align 8
|
|
%14 = getelementptr inbounds %uint128, ptr %4, i64 0, i32 1
|
|
store i64 %6, ptr %14, align 8
|
|
ret i1 %13
|
|
}
|
|
|
|
|
|
%uint192 = type { i64, i64, i64 }
|
|
|
|
define void @add_U192_without_i128_or(ptr sret(%uint192) %0, i64 %1, i64 %2, i64 %3, i64 %4, i64 %5, i64 %6) nounwind {
|
|
; CHECK-LABEL: add_U192_without_i128_or:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: addq %r8, %rsi
|
|
; CHECK-NEXT: adcq %r9, %rdx
|
|
; CHECK-NEXT: adcq {{[0-9]+}}(%rsp), %rcx
|
|
; CHECK-NEXT: movq %rcx, (%rdi)
|
|
; CHECK-NEXT: movq %rdx, 8(%rdi)
|
|
; CHECK-NEXT: movq %rsi, 16(%rdi)
|
|
; CHECK-NEXT: retq
|
|
%8 = add i64 %4, %1
|
|
%9 = icmp ult i64 %8, %1
|
|
%10 = add i64 %5, %2
|
|
%11 = icmp ult i64 %10, %2
|
|
%12 = zext i1 %9 to i64
|
|
%13 = add i64 %10, %12
|
|
%14 = icmp ult i64 %13, %10
|
|
%15 = or i1 %11, %14
|
|
%16 = add i64 %6, %3
|
|
%17 = zext i1 %15 to i64
|
|
%18 = add i64 %16, %17
|
|
store i64 %18, ptr %0, align 8
|
|
%19 = getelementptr inbounds %uint192, ptr %0, i64 0, i32 1
|
|
store i64 %13, ptr %19, align 8
|
|
%20 = getelementptr inbounds %uint192, ptr %0, i64 0, i32 2
|
|
store i64 %8, ptr %20, align 8
|
|
ret void
|
|
}
|
|
|
|
|
|
%uint256 = type { %uint128, %uint128 }
|
|
|
|
; Classic unrolled 256-bit addition implementation using i64 as the word type.
|
|
; It starts by adding least significant words and propagates carry to additions of the higher words.
|
|
define void @add_U256_without_i128_or_by_i64_words(ptr sret(%uint256) %0, ptr %1, ptr %2) nounwind {
|
|
; CHECK-LABEL: add_U256_without_i128_or_by_i64_words:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: movq (%rdx), %rcx
|
|
; CHECK-NEXT: movq 8(%rdx), %rdi
|
|
; CHECK-NEXT: addq (%rsi), %rcx
|
|
; CHECK-NEXT: adcq 8(%rsi), %rdi
|
|
; CHECK-NEXT: movq 16(%rdx), %r8
|
|
; CHECK-NEXT: adcq 16(%rsi), %r8
|
|
; CHECK-NEXT: movq 24(%rdx), %rdx
|
|
; CHECK-NEXT: adcq 24(%rsi), %rdx
|
|
; CHECK-NEXT: movq %rdx, (%rax)
|
|
; CHECK-NEXT: movq %r8, 8(%rax)
|
|
; CHECK-NEXT: movq %rdi, 16(%rax)
|
|
; CHECK-NEXT: movq %rcx, 24(%rax)
|
|
; CHECK-NEXT: retq
|
|
%4 = load i64, ptr %1, align 8
|
|
%5 = load i64, ptr %2, align 8
|
|
%6 = add i64 %5, %4
|
|
%7 = icmp ult i64 %6, %4
|
|
%8 = getelementptr inbounds %uint256, ptr %1, i64 0, i32 0, i32 1
|
|
%9 = load i64, ptr %8, align 8
|
|
%10 = getelementptr inbounds %uint256, ptr %2, i64 0, i32 0, i32 1
|
|
%11 = load i64, ptr %10, align 8
|
|
%12 = add i64 %11, %9
|
|
%13 = icmp ult i64 %12, %9
|
|
%14 = zext i1 %7 to i64
|
|
%15 = add i64 %12, %14
|
|
%16 = icmp ult i64 %15, %14
|
|
%17 = or i1 %13, %16
|
|
%18 = getelementptr inbounds %uint256, ptr %1, i64 0, i32 1, i32 0
|
|
%19 = load i64, ptr %18, align 8
|
|
%20 = getelementptr inbounds %uint256, ptr %2, i64 0, i32 1, i32 0
|
|
%21 = load i64, ptr %20, align 8
|
|
%22 = add i64 %21, %19
|
|
%23 = icmp ult i64 %22, %19
|
|
%24 = zext i1 %17 to i64
|
|
%25 = add i64 %22, %24
|
|
%26 = icmp ult i64 %25, %24
|
|
%27 = or i1 %23, %26
|
|
%28 = getelementptr inbounds %uint256, ptr %1, i64 0, i32 1, i32 1
|
|
%29 = load i64, ptr %28, align 8
|
|
%30 = getelementptr inbounds %uint256, ptr %2, i64 0, i32 1, i32 1
|
|
%31 = load i64, ptr %30, align 8
|
|
%32 = add i64 %31, %29
|
|
%33 = zext i1 %27 to i64
|
|
%34 = add i64 %32, %33
|
|
store i64 %34, ptr %0, align 8
|
|
%35 = getelementptr inbounds %uint256, ptr %0, i64 0, i32 0, i32 1
|
|
store i64 %25, ptr %35, align 8
|
|
%36 = getelementptr inbounds %uint256, ptr %0, i64 0, i32 1, i32 0
|
|
store i64 %15, ptr %36, align 8
|
|
%37 = getelementptr inbounds %uint256, ptr %0, i64 0, i32 1, i32 1
|
|
store i64 %6, ptr %37, align 8
|
|
ret void
|
|
}
|
|
|
|
; The 256-bit addition implementation using two inlined uaddo procedures for U128 type { i64, i64 }.
|
|
; This is similar to how LLVM legalize types in CodeGen.
|
|
define void @add_U256_without_i128_or_recursive(ptr sret(%uint256) %0, ptr %1, ptr %2) nounwind {
|
|
; CHECK-LABEL: add_U256_without_i128_or_recursive:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rdi, %rax
|
|
; CHECK-NEXT: movq (%rdx), %rcx
|
|
; CHECK-NEXT: movq 8(%rdx), %rdi
|
|
; CHECK-NEXT: addq (%rsi), %rcx
|
|
; CHECK-NEXT: adcq 8(%rsi), %rdi
|
|
; CHECK-NEXT: movq 16(%rdx), %r8
|
|
; CHECK-NEXT: movq 24(%rdx), %rdx
|
|
; CHECK-NEXT: adcq 16(%rsi), %r8
|
|
; CHECK-NEXT: adcq 24(%rsi), %rdx
|
|
; CHECK-NEXT: movq %rcx, (%rax)
|
|
; CHECK-NEXT: movq %rdi, 8(%rax)
|
|
; CHECK-NEXT: movq %r8, 16(%rax)
|
|
; CHECK-NEXT: movq %rdx, 24(%rax)
|
|
; CHECK-NEXT: retq
|
|
%4 = load i64, ptr %1, align 8
|
|
%5 = getelementptr inbounds %uint256, ptr %1, i64 0, i32 0, i32 1
|
|
%6 = load i64, ptr %5, align 8
|
|
%7 = load i64, ptr %2, align 8
|
|
%8 = getelementptr inbounds %uint256, ptr %2, i64 0, i32 0, i32 1
|
|
%9 = load i64, ptr %8, align 8
|
|
%10 = add i64 %7, %4
|
|
%11 = icmp ult i64 %10, %4
|
|
%12 = add i64 %9, %6
|
|
%13 = icmp ult i64 %12, %6
|
|
%14 = zext i1 %11 to i64
|
|
%15 = add i64 %12, %14
|
|
%16 = icmp ult i64 %15, %12
|
|
%17 = or i1 %13, %16
|
|
%18 = getelementptr inbounds %uint256, ptr %1, i64 0, i32 1, i32 0
|
|
%19 = load i64, ptr %18, align 8
|
|
%20 = getelementptr inbounds %uint256, ptr %1, i64 0, i32 1, i32 1
|
|
%21 = load i64, ptr %20, align 8
|
|
%22 = getelementptr inbounds %uint256, ptr %2, i64 0, i32 1, i32 0
|
|
%23 = load i64, ptr %22, align 8
|
|
%24 = getelementptr inbounds %uint256, ptr %2, i64 0, i32 1, i32 1
|
|
%25 = load i64, ptr %24, align 8
|
|
%26 = add i64 %23, %19
|
|
%27 = icmp ult i64 %26, %19
|
|
%28 = add i64 %25, %21
|
|
%29 = zext i1 %27 to i64
|
|
%30 = add i64 %28, %29
|
|
%31 = zext i1 %17 to i64
|
|
%32 = add i64 %26, %31
|
|
%33 = icmp ult i64 %32, %26
|
|
%34 = zext i1 %33 to i64
|
|
%35 = add i64 %30, %34
|
|
store i64 %10, ptr %0, align 8
|
|
%36 = getelementptr inbounds %uint256, ptr %0, i64 0, i32 0, i32 1
|
|
store i64 %15, ptr %36, align 8
|
|
%37 = getelementptr inbounds %uint256, ptr %0, i64 0, i32 1, i32 0
|
|
store i64 %32, ptr %37, align 8
|
|
%38 = getelementptr inbounds %uint256, ptr %0, i64 0, i32 1, i32 1
|
|
store i64 %35, ptr %38, align 8
|
|
ret void
|
|
}
|
|
|
|
define i32 @addcarry_ult(i32 %a, i32 %b, i32 %x, i32 %y) nounwind {
|
|
; CHECK-LABEL: addcarry_ult:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movl %edi, %eax
|
|
; CHECK-NEXT: cmpl %ecx, %edx
|
|
; CHECK-NEXT: adcl %esi, %eax
|
|
; CHECK-NEXT: retq
|
|
%s = add i32 %a, %b
|
|
%k = icmp ult i32 %x, %y
|
|
%z = zext i1 %k to i32
|
|
%r = add i32 %s, %z
|
|
ret i32 %r
|
|
}
|
|
|
|
define i32 @addcarry_ugt(i32 %a, i32 %b, i32 %x, i32 %y) nounwind {
|
|
; CHECK-LABEL: addcarry_ugt:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movl %edi, %eax
|
|
; CHECK-NEXT: cmpl %edx, %ecx
|
|
; CHECK-NEXT: adcl %esi, %eax
|
|
; CHECK-NEXT: retq
|
|
%s = add i32 %a, %b
|
|
%k = icmp ugt i32 %x, %y
|
|
%z = zext i1 %k to i32
|
|
%r = add i32 %s, %z
|
|
ret i32 %r
|
|
}
|
|
|
|
define i32 @addcarry_ule(i32 %a, i32 %b, i32 %x, i32 %y) nounwind {
|
|
; CHECK-LABEL: addcarry_ule:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: # kill: def $esi killed $esi def $rsi
|
|
; CHECK-NEXT: # kill: def $edi killed $edi def $rdi
|
|
; CHECK-NEXT: leal (%rdi,%rsi), %eax
|
|
; CHECK-NEXT: cmpl %edx, %ecx
|
|
; CHECK-NEXT: sbbl $-1, %eax
|
|
; CHECK-NEXT: retq
|
|
%s = add i32 %a, %b
|
|
%k = icmp ule i32 %x, %y
|
|
%z = zext i1 %k to i32
|
|
%r = add i32 %s, %z
|
|
ret i32 %r
|
|
}
|
|
|
|
define i32 @addcarry_uge(i32 %a, i32 %b, i32 %x, i32 %y) nounwind {
|
|
; CHECK-LABEL: addcarry_uge:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: # kill: def $esi killed $esi def $rsi
|
|
; CHECK-NEXT: # kill: def $edi killed $edi def $rdi
|
|
; CHECK-NEXT: leal (%rdi,%rsi), %eax
|
|
; CHECK-NEXT: cmpl %ecx, %edx
|
|
; CHECK-NEXT: sbbl $-1, %eax
|
|
; CHECK-NEXT: retq
|
|
%s = add i32 %a, %b
|
|
%k = icmp uge i32 %x, %y
|
|
%z = zext i1 %k to i32
|
|
%r = add i32 %s, %z
|
|
ret i32 %r
|
|
}
|
|
|
|
define { i64, i64 } @addcarry_commutative_1(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
|
|
; CHECK-LABEL: addcarry_commutative_1:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rsi, %rax
|
|
; CHECK-NEXT: addq %rdx, %rdi
|
|
; CHECK-NEXT: adcq %rcx, %rax
|
|
; CHECK-NEXT: movq %rax, %rdx
|
|
; CHECK-NEXT: retq
|
|
%z0 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x0, i64 %y0)
|
|
%k0 = extractvalue { i64, i1 } %z0, 1
|
|
%k0z = zext i1 %k0 to i64
|
|
|
|
%t1s = add i64 %x1, %y1
|
|
%z1s = add i64 %t1s, %k0z
|
|
|
|
; same as the above, but args swapped
|
|
%a1s = add i64 %y1, %x1
|
|
%b1s = add i64 %a1s, %k0z
|
|
|
|
%r0 = insertvalue { i64, i64 } poison, i64 %z1s, 0
|
|
%r1 = insertvalue { i64, i64 } %r0, i64 %b1s, 1
|
|
ret { i64, i64 } %r1
|
|
}
|
|
|
|
define { i64, i64 } @addcarry_commutative_2(i64 %x0, i64 %x1, i64 %y0, i64 %y1) nounwind {
|
|
; CHECK-LABEL: addcarry_commutative_2:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: movq %rsi, %rax
|
|
; CHECK-NEXT: addq %rdx, %rdi
|
|
; CHECK-NEXT: adcq %rcx, %rax
|
|
; CHECK-NEXT: movq %rax, %rdx
|
|
; CHECK-NEXT: retq
|
|
%z0 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x0, i64 %y0)
|
|
%k0 = extractvalue { i64, i1 } %z0, 1
|
|
%k0z = zext i1 %k0 to i64
|
|
|
|
%t1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %x1, i64 %y1)
|
|
%t1s = extractvalue { i64, i1 } %t1, 0
|
|
%z1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %t1s, i64 %k0z)
|
|
%z1s = extractvalue { i64, i1 } %z1, 0
|
|
|
|
; same as the above, but args swapped
|
|
%a1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %y1, i64 %x1)
|
|
%a1s = extractvalue { i64, i1 } %a1, 0
|
|
%b1 = call { i64, i1 } @llvm.uadd.with.overflow.i64(i64 %a1s, i64 %k0z)
|
|
%b1s = extractvalue { i64, i1 } %b1, 0
|
|
|
|
%r0 = insertvalue { i64, i64 } poison, i64 %z1s, 0
|
|
%r1 = insertvalue { i64, i64 } %r0, i64 %b1s, 1
|
|
ret { i64, i64 } %r1
|
|
}
|
|
|
|
define i1 @pr84831(i64 %arg) {
|
|
; CHECK-LABEL: pr84831:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: testq %rdi, %rdi
|
|
; CHECK-NEXT: setne %al
|
|
; CHECK-NEXT: xorl %ecx, %ecx
|
|
; CHECK-NEXT: addb $-1, %al
|
|
; CHECK-NEXT: adcq $1, %rcx
|
|
; CHECK-NEXT: setb %al
|
|
; CHECK-NEXT: retq
|
|
%a = icmp ult i64 0, %arg
|
|
%add1 = add i64 0, 1
|
|
%carryout1 = icmp ult i64 %add1, 0
|
|
%b = zext i1 %a to i64
|
|
%add2 = add i64 %add1, %b
|
|
%carryout2 = icmp ult i64 %add2, %add1
|
|
%zc1 = zext i1 %carryout1 to i63
|
|
%zc2 = zext i1 %carryout2 to i63
|
|
%or = or i63 %zc1, %zc2
|
|
%trunc = trunc i63 %or to i1
|
|
ret i1 %trunc
|
|
}
|