Skip to content

Commit 140adc9

Browse files
LuoYuankeYuanke Luo
andauthored
[X86] Improve illegal return type handling in FastISel (#186723)
Previously, FastISel would fall back to DAG ISel for any illegal return type. This change adds a more precise check to determine if the ABI requires a type conversion that FastISel cannot handle. For example, bfloat is returned as f16 in XMM0, but FastISel would assign f32 register type and store it in FuncInfo.ValueMap, causing DAG to incorrectly perform type conversion from f32 to bfloat later. However, i1 is promoted to i8 and returned as i8 per the ABI, so FastISel can safely lower it without switching to DAGISel. This change enables FastISel to handle such cases properly. --------- Co-authored-by: Yuanke Luo <ykluo@birentech.com>
1 parent 55b271d commit 140adc9

2 files changed

Lines changed: 127 additions & 5 deletions

File tree

llvm/lib/Target/X86/X86FastISel.cpp

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3200,11 +3200,27 @@ bool X86FastISel::fastLowerCall(CallLoweringInfo &CLI) {
32003200
bool Is64Bit = Subtarget->is64Bit();
32013201
bool IsWin64 = Subtarget->isCallingConvWin64(CC);
32023202

3203-
// If the return type is illegal, don't bother to promote it, just fall back
3204-
// to DAG ISel.
3205-
MVT RetVT;
3206-
if (!isTypeLegal(CLI.RetTy, RetVT) && !CLI.RetTy->isVoidTy())
3207-
return false;
3203+
// If the return type is illegal, check if the ABI requires a type conversion
3204+
// that FastISel cannot handle. Fall back to DAG ISel in such cases.
3205+
// For example, bfloat is returned as f16 in XMM0, however FastISel would
3206+
// assign f32 register type and store it in FuncInfo.ValueMap. This would
3207+
// cause DAG incorrectly perform type conversion from f32 to bfloat after get
3208+
// the value from FuncInfo.ValueMap.
3209+
// However, i1 is promoted to i8 and return i8 defined by ABI, so FastISel can
3210+
// lower it without switching to DAGISel.
3211+
MVT RetVT = MVT::Other;
3212+
if (!isTypeLegal(CLI.RetTy, RetVT) && !CLI.RetTy->isVoidTy()) {
3213+
if (RetVT == MVT::Other)
3214+
return false; // Unknown type, let DAG ISel handle it.
3215+
3216+
// RetVT is not MVT::Other, it must be simple now. It is something rely on
3217+
// the logic of isTypeLegal().
3218+
MVT ABIVT = TLI.getRegisterTypeForCallingConv(CLI.RetTy->getContext(),
3219+
CLI.CallConv, RetVT);
3220+
MVT RegVT = TLI.getRegisterType(CLI.RetTy->getContext(), RetVT);
3221+
if (ABIVT != RegVT)
3222+
return false;
3223+
}
32083224

32093225
// Call / invoke instructions with NoCfCheck attribute require special
32103226
// handling.
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
2+
; RUN: llc --fast-isel < %s -mtriple=x86_64-unknown-unknown | FileCheck %s
3+
4+
define i8 @test_direct_call(ptr %f) nounwind {
5+
; CHECK-LABEL: test_direct_call:
6+
; CHECK: # %bb.0: # %entry
7+
; CHECK-NEXT: pushq %rax
8+
; CHECK-NEXT: callq foo@PLT
9+
; CHECK-NEXT: movzbl %al, %edi
10+
; CHECK-NEXT: callq bar@PLT
11+
; CHECK-NEXT: popq %rcx
12+
; CHECK-NEXT: retq
13+
entry:
14+
%call = call i1 @foo(ptr %f)
15+
%call2 = call zeroext i8 @bar(i1 %call)
16+
ret i8 %call2
17+
}
18+
19+
define i8 @test_fast_direct_call(ptr %f) nounwind {
20+
; CHECK-LABEL: test_fast_direct_call:
21+
; CHECK: # %bb.0: # %entry
22+
; CHECK-NEXT: pushq %rax
23+
; CHECK-NEXT: callq foo_fast@PLT
24+
; CHECK-NEXT: movzbl %al, %edi
25+
; CHECK-NEXT: callq bar@PLT
26+
; CHECK-NEXT: popq %rcx
27+
; CHECK-NEXT: retq
28+
entry:
29+
%call = call fastcc i1 @foo_fast(ptr %f)
30+
%call2 = call zeroext i8 @bar(i1 %call)
31+
ret i8 %call2
32+
}
33+
34+
define i8 @test_indirect_all(ptr %fptr, ptr %f) nounwind {
35+
; CHECK-LABEL: test_indirect_all:
36+
; CHECK: # %bb.0: # %entry
37+
; CHECK-NEXT: pushq %rbx
38+
; CHECK-NEXT: movq %rdi, %rbx
39+
; CHECK-NEXT: movq %rsi, %rdi
40+
; CHECK-NEXT: callq foo@PLT
41+
; CHECK-NEXT: movzbl %al, %edi
42+
; CHECK-NEXT: callq *%rbx
43+
; CHECK-NEXT: popq %rbx
44+
; CHECK-NEXT: retq
45+
entry:
46+
%call = call i1 @foo(ptr %f)
47+
%call2 = call zeroext i8 %fptr(i1 %call)
48+
ret i8 %call2
49+
}
50+
51+
define i8 @test_indirect_all2(ptr %fptr, ptr %f, i1 %cond) nounwind {
52+
; CHECK-LABEL: test_indirect_all2:
53+
; CHECK: # %bb.0: # %entry
54+
; CHECK-NEXT: pushq %rbp
55+
; CHECK-NEXT: pushq %rbx
56+
; CHECK-NEXT: pushq %rax
57+
; CHECK-NEXT: movl %edx, %ebp
58+
; CHECK-NEXT: movq %rdi, %rbx
59+
; CHECK-NEXT: movq %rsi, %rdi
60+
; CHECK-NEXT: callq foo@PLT
61+
; CHECK-NEXT: testb $1, %bpl
62+
; CHECK-NEXT: je .LBB3_2
63+
; CHECK-NEXT: # %bb.1: # %exit
64+
; CHECK-NEXT: movzbl %al, %edi
65+
; CHECK-NEXT: callq *%rbx
66+
; CHECK-NEXT: jmp .LBB3_3
67+
; CHECK-NEXT: .LBB3_2: # %exit2
68+
; CHECK-NEXT: movb $3, %al
69+
; CHECK-NEXT: .LBB3_3: # %exit2
70+
; CHECK-NEXT: addq $8, %rsp
71+
; CHECK-NEXT: popq %rbx
72+
; CHECK-NEXT: popq %rbp
73+
; CHECK-NEXT: retq
74+
entry:
75+
%call = call i1 @foo(ptr %f)
76+
br i1 %cond, label %exit, label %exit2
77+
78+
exit:
79+
%call2 = call zeroext i8 %fptr(i1 %call)
80+
ret i8 %call2
81+
82+
exit2:
83+
ret i8 3
84+
}
85+
86+
87+
define i8 @test_fast_indirect_all(ptr %fptr, ptr %f) nounwind {
88+
; CHECK-LABEL: test_fast_indirect_all:
89+
; CHECK: # %bb.0: # %entry
90+
; CHECK-NEXT: pushq %rbx
91+
; CHECK-NEXT: movq %rdi, %rbx
92+
; CHECK-NEXT: movq %rsi, %rdi
93+
; CHECK-NEXT: callq foo@PLT
94+
; CHECK-NEXT: movzbl %al, %edi
95+
; CHECK-NEXT: callq *%rbx
96+
; CHECK-NEXT: popq %rbx
97+
; CHECK-NEXT: retq
98+
entry:
99+
%call = call fastcc i1 @foo(ptr %f)
100+
%call2 = call zeroext i8 %fptr(i1 %call)
101+
ret i8 %call2
102+
}
103+
104+
declare i1 @foo(ptr %f)
105+
declare zeroext i8 @bar(i1)
106+
declare fastcc i1 @foo_fast(ptr %f)

0 commit comments

Comments
 (0)