
M68k's SETCC instruction (`scc`) distinctly fills the destination byte with all 1s. If boolean contents are set to `ZeroOrOneBooleanContent`, LLVM can mistakenly think the destination holds `0x01` instead of `0xff` and emit broken code as a result. This change corrects the boolean content type to `ZeroOrNegativeOneBooleanContent`. For example, this IR: ```llvm define dso_local signext range(i8 0, 2) i8 @testBool(i32 noundef %a) local_unnamed_addr #0 { entry: %cmp = icmp eq i32 %a, 4660 %. = zext i1 %cmp to i8 ret i8 %. } ``` would previously build as: ```asm testBool: ; @testBool cmpi.l #4660, (4,%sp) seq %d0 and.l #255, %d0 rts ``` Notice the `zext` is erroneously not clearing the low bits, and thus the register returns with 255 instead of 1. This patch fixes the issue: ```asm testBool: ; @testBool cmpi.l #4660, (4,%sp) seq %d0 and.l #1, %d0 rts ``` Most of the tests containing `scc` suffered from the same value error as described above, so those tests have been updated to match the new output (which also logically corrects them).
168 lines
5.1 KiB
LLVM
168 lines
5.1 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
|
; RUN: llc < %s -mtriple=m68k-linux -verify-machineinstrs | FileCheck %s
|
|
|
|
define zeroext i8 @smul_i8(i8 signext %a, i8 signext %b) nounwind ssp {
|
|
; CHECK-LABEL: smul_i8:
|
|
; CHECK: ; %bb.0: ; %entry
|
|
; CHECK-NEXT: move.b (11,%sp), %d0
|
|
; CHECK-NEXT: and.l #255, %d0
|
|
; CHECK-NEXT: move.b (7,%sp), %d1
|
|
; CHECK-NEXT: and.l #255, %d1
|
|
; CHECK-NEXT: muls %d0, %d1
|
|
; CHECK-NEXT: move.l %d1, %d0
|
|
; CHECK-NEXT: and.l #65535, %d0
|
|
; CHECK-NEXT: and.l #255, %d0
|
|
; CHECK-NEXT: rts
|
|
entry:
|
|
%smul = tail call { i8, i1 } @llvm.smul.with.overflow.i8(i8 %a, i8 %b)
|
|
%cmp = extractvalue { i8, i1 } %smul, 1
|
|
%smul.result = extractvalue { i8, i1 } %smul, 0
|
|
%X = select i1 %cmp, i8 42, i8 %smul.result
|
|
ret i8 %X
|
|
}
|
|
|
|
define zeroext i8 @smul_i8_no_ovf(i8 signext %a, i8 signext %b) nounwind ssp {
|
|
; CHECK-LABEL: smul_i8_no_ovf:
|
|
; CHECK: ; %bb.0: ; %entry
|
|
; CHECK-NEXT: moveq #42, %d0
|
|
; CHECK-NEXT: rts
|
|
entry:
|
|
%smul = tail call { i8, i1 } @llvm.smul.with.overflow.i8(i8 %a, i8 %b)
|
|
%cmp = extractvalue { i8, i1 } %smul, 1
|
|
%smul.result = extractvalue { i8, i1 } %smul, 0
|
|
%X = select i1 %cmp, i8 %smul.result, i8 42
|
|
ret i8 %X
|
|
}
|
|
|
|
declare { i8, i1 } @llvm.smul.with.overflow.i8(i8, i8) nounwind readnone
|
|
|
|
define zeroext i16 @smul_i16(i16 signext %a, i16 signext %b) nounwind ssp {
|
|
; CHECK-LABEL: smul_i16:
|
|
; CHECK: ; %bb.0: ; %entry
|
|
; CHECK-NEXT: move.w (6,%sp), %d0
|
|
; CHECK-NEXT: move.w (10,%sp), %d1
|
|
; CHECK-NEXT: muls %d1, %d0
|
|
; CHECK-NEXT: and.l #65535, %d0
|
|
; CHECK-NEXT: rts
|
|
entry:
|
|
%smul = tail call { i16, i1 } @llvm.smul.with.overflow.i16(i16 %a, i16 %b)
|
|
%cmp = extractvalue { i16, i1 } %smul, 1
|
|
%smul.result = extractvalue { i16, i1 } %smul, 0
|
|
%X = select i1 %cmp, i16 42, i16 %smul.result
|
|
ret i16 %X
|
|
}
|
|
|
|
declare { i16, i1 } @llvm.smul.with.overflow.i16(i16, i16) nounwind readnone
|
|
|
|
declare i32 @printf(ptr, ...) nounwind
|
|
declare {i32, i1} @llvm.smul.with.overflow.i32(i32, i32)
|
|
|
|
@ok = internal constant [4 x i8] c"%d\0A\00"
|
|
@no = internal constant [4 x i8] c"no\0A\00"
|
|
|
|
define fastcc i1 @test1(i32 %v1, i32 %v2) nounwind {
|
|
; CHECK-LABEL: test1:
|
|
; CHECK: ; %bb.0: ; %entry
|
|
; CHECK-NEXT: suba.l #12, %sp
|
|
; CHECK-NEXT: muls.l %d1, %d0
|
|
; CHECK-NEXT: bvc .LBB3_1
|
|
; CHECK-NEXT: ; %bb.2: ; %overflow
|
|
; CHECK-NEXT: lea (no,%pc), %a0
|
|
; CHECK-NEXT: move.l %a0, (%sp)
|
|
; CHECK-NEXT: jsr printf
|
|
; CHECK-NEXT: moveq #0, %d0
|
|
; CHECK-NEXT: adda.l #12, %sp
|
|
; CHECK-NEXT: rts
|
|
; CHECK-NEXT: .LBB3_1: ; %normal
|
|
; CHECK-NEXT: move.l %d0, (4,%sp)
|
|
; CHECK-NEXT: lea (ok,%pc), %a0
|
|
; CHECK-NEXT: move.l %a0, (%sp)
|
|
; CHECK-NEXT: jsr printf
|
|
; CHECK-NEXT: moveq #1, %d0
|
|
; CHECK-NEXT: adda.l #12, %sp
|
|
; CHECK-NEXT: rts
|
|
entry:
|
|
%t = call {i32, i1} @llvm.smul.with.overflow.i32(i32 %v1, i32 %v2)
|
|
%sum = extractvalue {i32, i1} %t, 0
|
|
%obit = extractvalue {i32, i1} %t, 1
|
|
br i1 %obit, label %overflow, label %normal
|
|
|
|
normal:
|
|
%t1 = tail call i32 (ptr, ...) @printf( ptr @ok, i32 %sum ) nounwind
|
|
ret i1 true
|
|
|
|
overflow:
|
|
%t2 = tail call i32 (ptr, ...) @printf( ptr @no ) nounwind
|
|
ret i1 false
|
|
}
|
|
|
|
define fastcc i1 @test2(i32 %v1, i32 %v2) nounwind {
|
|
; CHECK-LABEL: test2:
|
|
; CHECK: ; %bb.0: ; %entry
|
|
; CHECK-NEXT: suba.l #12, %sp
|
|
; CHECK-NEXT: muls.l %d1, %d0
|
|
; CHECK-NEXT: svs %d1
|
|
; CHECK-NEXT: and.b #1, %d1
|
|
; CHECK-NEXT: cmpi.b #0, %d1
|
|
; CHECK-NEXT: beq .LBB4_2
|
|
; CHECK-NEXT: ; %bb.1: ; %overflow
|
|
; CHECK-NEXT: lea (no,%pc), %a0
|
|
; CHECK-NEXT: move.l %a0, (%sp)
|
|
; CHECK-NEXT: jsr printf
|
|
; CHECK-NEXT: moveq #0, %d0
|
|
; CHECK-NEXT: adda.l #12, %sp
|
|
; CHECK-NEXT: rts
|
|
; CHECK-NEXT: .LBB4_2: ; %normal
|
|
; CHECK-NEXT: move.l %d0, (4,%sp)
|
|
; CHECK-NEXT: lea (ok,%pc), %a0
|
|
; CHECK-NEXT: move.l %a0, (%sp)
|
|
; CHECK-NEXT: jsr printf
|
|
; CHECK-NEXT: moveq #1, %d0
|
|
; CHECK-NEXT: adda.l #12, %sp
|
|
; CHECK-NEXT: rts
|
|
entry:
|
|
%t = call {i32, i1} @llvm.smul.with.overflow.i32(i32 %v1, i32 %v2)
|
|
%sum = extractvalue {i32, i1} %t, 0
|
|
%obit = extractvalue {i32, i1} %t, 1
|
|
br i1 %obit, label %overflow, label %normal
|
|
|
|
overflow:
|
|
%t2 = tail call i32 (ptr, ...) @printf( ptr @no ) nounwind
|
|
ret i1 false
|
|
|
|
normal:
|
|
%t1 = tail call i32 (ptr, ...) @printf( ptr @ok, i32 %sum ) nounwind
|
|
ret i1 true
|
|
}
|
|
|
|
define i32 @test3(i32 %a, i32 %b) nounwind readnone {
|
|
; CHECK-LABEL: test3:
|
|
; CHECK: ; %bb.0: ; %entry
|
|
; CHECK-NEXT: move.l (8,%sp), %d0
|
|
; CHECK-NEXT: add.l (4,%sp), %d0
|
|
; CHECK-NEXT: add.l %d0, %d0
|
|
; CHECK-NEXT: rts
|
|
entry:
|
|
%tmp0 = add i32 %b, %a
|
|
%tmp1 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 %tmp0, i32 2)
|
|
%tmp2 = extractvalue { i32, i1 } %tmp1, 0
|
|
ret i32 %tmp2
|
|
}
|
|
|
|
; Same as umul-with-overflow, we shouldn't fallback to
|
|
; builtin here
|
|
define i32 @test4(i32 %a, i32 %b) nounwind readnone {
|
|
; CHECK-LABEL: test4:
|
|
; CHECK: ; %bb.0: ; %entry
|
|
; CHECK-NEXT: move.l (8,%sp), %d0
|
|
; CHECK-NEXT: add.l (4,%sp), %d0
|
|
; CHECK-NEXT: moveq #4, %d1
|
|
; CHECK-NEXT: muls.l %d1, %d0
|
|
; CHECK-NEXT: rts
|
|
entry:
|
|
%tmp0 = add i32 %b, %a
|
|
%tmp1 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 %tmp0, i32 4)
|
|
%tmp2 = extractvalue { i32, i1 } %tmp1, 0
|
|
ret i32 %tmp2
|
|
}
|