Generate SSA code from returns (#5172)

Modify return pseudo-instructions to have pairs of registers: virtual and real. This allows us to constrain the virtual registers to the real ones specified by the abi, instead of directly emitting moves to those real registers.
This commit is contained in:
Trevor Elliott
2022-11-08 16:00:49 -08:00
committed by GitHub
parent d59caf39b6
commit b077854b57
171 changed files with 6368 additions and 6364 deletions

View File

@@ -398,7 +398,7 @@ impl ABIMachineSpec for AArch64MachineDeps {
Inst::Args { args }
}
fn gen_ret(setup_frame: bool, isa_flags: &aarch64_settings::Flags, rets: Vec<Reg>) -> Inst {
fn gen_ret(setup_frame: bool, isa_flags: &aarch64_settings::Flags, rets: Vec<RetPair>) -> Inst {
if isa_flags.sign_return_address() && (setup_frame || isa_flags.sign_return_address_all()) {
let key = if isa_flags.sign_return_address_with_bkey() {
APIKey::B

View File

@@ -787,7 +787,7 @@
;; A machine return instruction.
(Ret
(rets VecReg))
(rets VecRetPair))
;; A machine return instruction with pointer authentication using SP as the
;; modifier. This instruction requires pointer authentication support
@@ -797,7 +797,7 @@
(AuthenticatedRet
(key APIKey)
(is_hint bool)
(rets VecReg))
(rets VecRetPair))
;; An unconditional branch.
(Jump

View File

@@ -1022,8 +1022,8 @@ fn aarch64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
}
}
&Inst::Ret { ref rets } | &Inst::AuthenticatedRet { ref rets, .. } => {
for &ret in rets {
collector.reg_use(ret);
for ret in rets {
collector.reg_fixed_use(ret.vreg, ret.preg);
}
}
&Inst::Jump { .. } => {}

View File

@@ -265,12 +265,15 @@ mod test {
let buffer = backend.compile_function(&mut func, false).unwrap().buffer;
let code = buffer.data();
// mov x3, #0x1234
// add w0, w0, w3
// ret
let golden = vec![
0x83, 0x46, 0x82, 0xd2, 0x00, 0x00, 0x03, 0x0b, 0xc0, 0x03, 0x5f, 0xd6,
];
// To update this comment, write the golden bytes to a file, and run the following command
// on it to update:
// > aarch64-linux-gnu-objdump -b binary -D <file> -m aarch64
//
// 0: d2824682 mov x2, #0x1234 // #4660
// 4: 0b020000 add w0, w0, w2
// 8: d65f03c0 ret
let golden = vec![130, 70, 130, 210, 0, 0, 2, 11, 192, 3, 95, 214];
assert_eq!(code, &golden[..]);
}
@@ -320,24 +323,28 @@ mod test {
.unwrap();
let code = result.buffer.data();
// mov x10, #0x1234 // #4660
// add w12, w0, w10
// mov w11, w12
// cbnz x11, 0x20
// mov x13, #0x1234 // #4660
// add w15, w12, w13
// mov w14, w15
// cbnz x14, 0x10
// mov w1, w12
// cbnz x1, 0x10
// mov x2, #0x1234 // #4660
// sub w0, w12, w2
// ret
// To update this comment, write the golden bytes to a file, and run the following command
// on it to update:
// > aarch64-linux-gnu-objdump -b binary -D <file> -m aarch64
//
// 0: d2824689 mov x9, #0x1234 // #4660
// 4: 0b09000b add w11, w0, w9
// 8: 2a0b03ea mov w10, w11
// c: b50000aa cbnz x10, 0x20
// 10: d282468c mov x12, #0x1234 // #4660
// 14: 0b0c016e add w14, w11, w12
// 18: 2a0e03ed mov w13, w14
// 1c: b5ffffad cbnz x13, 0x10
// 20: 2a0b03e0 mov w0, w11
// 24: b5ffff60 cbnz x0, 0x10
// 28: d2824681 mov x1, #0x1234 // #4660
// 2c: 4b010160 sub w0, w11, w1
// 30: d65f03c0 ret
let golden = vec![
138, 70, 130, 210, 12, 0, 10, 11, 235, 3, 12, 42, 171, 0, 0, 181, 141, 70, 130, 210,
143, 1, 13, 11, 238, 3, 15, 42, 174, 255, 255, 181, 225, 3, 12, 42, 97, 255, 255, 181,
130, 70, 130, 210, 128, 1, 2, 75, 192, 3, 95, 214,
137, 70, 130, 210, 11, 0, 9, 11, 234, 3, 11, 42, 170, 0, 0, 181, 140, 70, 130, 210,
110, 1, 12, 11, 237, 3, 14, 42, 173, 255, 255, 181, 224, 3, 11, 42, 96, 255, 255, 181,
129, 70, 130, 210, 96, 1, 1, 75, 192, 3, 95, 214,
];
assert_eq!(code, &golden[..]);
@@ -393,14 +400,18 @@ mod test {
.unwrap();
let code = result.buffer.data();
// To update this comment, write the golden bytes to a file, and run the following command
// on it to update:
// > aarch64-linux-gnu-objdump -b binary -D <file> -m aarch64
//
// 0: 7100081f cmp w0, #0x2
// 4: 54000122 b.cs 0x28 // b.hs, b.nlast
// 8: 9a8023e9 csel x9, xzr, x0, cs // cs = hs, nlast
// 8: 9a8023e8 csel x8, xzr, x0, cs // cs = hs, nlast
// c: d503229f csdb
// 10: 10000088 adr x8, 0x1c
// 14: b8a95909 ldrsw x9, [x8, w9, uxtw #2]
// 18: 8b090108 add x8, x8, x9
// 1c: d61f0100 br x8
// 10: 10000087 adr x7, 0x20
// 14: b8a858e8 ldrsw x8, [x7, w8, uxtw #2]
// 18: 8b0800e7 add x7, x7, x8
// 1c: d61f00e0 br x7
// 20: 00000010 udf #16
// 24: 00000018 udf #24
// 28: d2800060 mov x0, #0x3 // #3
@@ -411,9 +422,10 @@ mod test {
// 3c: d65f03c0 ret
let golden = vec![
31, 8, 0, 113, 34, 1, 0, 84, 233, 35, 128, 154, 159, 34, 3, 213, 136, 0, 0, 16, 9, 89,
169, 184, 8, 1, 9, 139, 0, 1, 31, 214, 16, 0, 0, 0, 24, 0, 0, 0, 96, 0, 128, 210, 192,
3, 95, 214, 32, 0, 128, 210, 192, 3, 95, 214, 64, 0, 128, 210, 192, 3, 95, 214,
31, 8, 0, 113, 34, 1, 0, 84, 232, 35, 128, 154, 159, 34, 3, 213, 135, 0, 0, 16, 232,
88, 168, 184, 231, 0, 8, 139, 224, 0, 31, 214, 16, 0, 0, 0, 24, 0, 0, 0, 96, 0, 128,
210, 192, 3, 95, 214, 32, 0, 128, 210, 192, 3, 95, 214, 64, 0, 128, 210, 192, 3, 95,
214,
];
assert_eq!(code, &golden[..]);

View File

@@ -235,7 +235,7 @@ impl ABIMachineSpec for Riscv64MachineDeps {
Inst::Args { args }
}
fn gen_ret(_setup_frame: bool, _isa_flags: &Self::F, rets: Vec<Reg>) -> Inst {
fn gen_ret(_setup_frame: bool, _isa_flags: &Self::F, rets: Vec<RetPair>) -> Inst {
Inst::Ret { rets }
}

View File

@@ -71,7 +71,7 @@
(Args
(args VecArgPair))
(Ret (rets VecReg))
(Ret (rets VecRetPair))
(Extend
(rd WritableReg)

View File

@@ -352,7 +352,9 @@ fn riscv64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut Operan
}
}
&Inst::Ret { ref rets } => {
collector.reg_uses(&rets[..]);
for ret in rets {
collector.reg_fixed_use(ret.vreg, ret.preg);
}
}
&Inst::Extend { rd, rn, .. } => {

View File

@@ -233,13 +233,18 @@ mod test {
);
let buffer = backend.compile_function(&mut func, true).unwrap();
let code = buffer.buffer.data();
// 0: 000015b7 lui a1,0x1
// 4: 23458593 addi a1,a1,564 # 0x1234
// 8: 00b5053b addw a0,a0,a1
// To update this comment, write the golden bytes to a file, and run the following command
// on it to update:
// > riscv64-linux-gnu-objdump -b binary -D <file> -m riscv
//
// 0: 000013b7 lui t2,0x1
// 4: 23438393 addi t2,t2,564 # 0x1234
// 8: 0075053b .4byte 0x75053b
// c: 00008067 ret
let golden = vec![
0xb7, 0x15, 0x0, 0x0, 0x93, 0x85, 0x45, 0x23, 0x3b, 0x5, 0xb5, 0x0, 0x67, 0x80, 0x0,
0x0,
183, 19, 0, 0, 147, 131, 67, 35, 59, 5, 117, 0, 103, 128, 0, 0,
];
assert_eq!(code, &golden[..]);
}

View File

@@ -457,7 +457,7 @@ impl ABIMachineSpec for S390xMachineDeps {
Inst::Args { args }
}
fn gen_ret(_setup_frame: bool, _isa_flags: &s390x_settings::Flags, rets: Vec<Reg>) -> Inst {
fn gen_ret(_setup_frame: bool, _isa_flags: &s390x_settings::Flags, rets: Vec<RetPair>) -> Inst {
Inst::Ret {
link: gpr(14),
rets,

View File

@@ -906,7 +906,7 @@
;; A machine return instruction.
(Ret
(link Reg)
(rets VecReg))
(rets VecRetPair))
;; An unconditional branch.
(Jump

View File

@@ -3480,7 +3480,7 @@ impl Inst {
}
&Inst::Call { link, ref info } => {
let link = allocs.next_writable(link);
debug_assert_eq!(link.to_reg(), gpr(14));
// Add relocation for TLS libcalls to enable linker optimizations.
match &info.tls_symbol {
@@ -3509,7 +3509,7 @@ impl Inst {
}
}
&Inst::CallInd { link, ref info } => {
let link = allocs.next_writable(link);
debug_assert_eq!(link.to_reg(), gpr(14));
let rn = allocs.next(info.rn);
let opcode = 0x0d; // BASR
@@ -3523,7 +3523,7 @@ impl Inst {
}
&Inst::Args { .. } => {}
&Inst::Ret { link, .. } => {
let link = allocs.next(link);
debug_assert_eq!(link, gpr(14));
let opcode = 0x07; // BCR
put(sink, &enc_rr(opcode, gpr(15), link));

View File

@@ -1009,17 +1009,17 @@ fn s390x_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandC
collector.reg_use(rn);
}
&Inst::Call { link, ref info } => {
collector.reg_def(link);
for u in &info.uses {
collector.reg_fixed_use(u.vreg, u.preg);
}
for d in &info.defs {
collector.reg_fixed_def(d.vreg, d.preg);
}
collector.reg_clobbers(info.clobbers);
let mut clobbers = info.clobbers.clone();
clobbers.add(link.to_reg().to_real_reg().unwrap().into());
collector.reg_clobbers(clobbers);
}
&Inst::CallInd { link, ref info } => {
collector.reg_def(link);
collector.reg_use(info.rn);
for u in &info.uses {
collector.reg_fixed_use(u.vreg, u.preg);
@@ -1027,16 +1027,21 @@ fn s390x_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandC
for d in &info.defs {
collector.reg_fixed_def(d.vreg, d.preg);
}
collector.reg_clobbers(info.clobbers);
let mut clobbers = info.clobbers.clone();
clobbers.add(link.to_reg().to_real_reg().unwrap().into());
collector.reg_clobbers(clobbers);
}
&Inst::Args { ref args } => {
for arg in args {
collector.reg_fixed_def(arg.vreg, arg.preg);
}
}
&Inst::Ret { link, ref rets } => {
collector.reg_use(link);
collector.reg_uses(&rets[..]);
&Inst::Ret { ref rets, .. } => {
// NOTE: we explicitly don't mark the link register as used here, as the use is only in
// the epilog where callee-save registers are restored.
for ret in rets {
collector.reg_fixed_use(ret.vreg, ret.preg);
}
}
&Inst::Jump { .. } => {}
&Inst::IndirectBr { rn, .. } => {
@@ -3231,7 +3236,7 @@ impl Inst {
format!("{} {}, {}", op, rd, rn)
}
&Inst::Call { link, ref info, .. } => {
let link = pretty_print_reg(link.to_reg(), allocs);
let link = link.to_reg();
let tls_symbol = match &info.tls_symbol {
None => "".to_string(),
Some(SymbolReloc::TlsGd { name }) => {
@@ -3239,12 +3244,19 @@ impl Inst {
}
_ => unreachable!(),
};
format!("brasl {}, {}{}", link, info.dest.display(None), tls_symbol)
debug_assert_eq!(link, gpr(14));
format!(
"brasl {}, {}{}",
show_reg(link),
info.dest.display(None),
tls_symbol
)
}
&Inst::CallInd { link, ref info, .. } => {
let link = pretty_print_reg(link.to_reg(), allocs);
let link = link.to_reg();
let rn = pretty_print_reg(info.rn, allocs);
format!("basr {}, {}", link, rn)
debug_assert_eq!(link, gpr(14));
format!("basr {}, {}", show_reg(link), rn)
}
&Inst::Args { ref args } => {
let mut s = "args".to_string();
@@ -3257,8 +3269,8 @@ impl Inst {
s
}
&Inst::Ret { link, .. } => {
let link = pretty_print_reg(link, allocs);
format!("br {}", link)
debug_assert_eq!(link, gpr(14));
format!("br {}", show_reg(link))
}
&Inst::Jump { dest } => {
let dest = dest.to_string();

View File

@@ -298,26 +298,25 @@ mod test {
// FIXME: the branching logic should be optimized more
// ahi %r2, 4660
// chi %r2, 0
// jglh label1 ; jg label2
// jg label6
// jg label3
// ahik %r3, %r2, 4660
// chi %r3, 0
// jglh label4 ; jg label5
// jg label3
// jg label6
// chi %r2, 0
// jglh label7 ; jg label8
// jg label3
// ahi %r2, -4660
// br %r14
// To update this comment, write the golden bytes to a file, and run the following command
// on it to update:
// > s390x-linux-gnu-objdump -b binary -D <file> -m s390
//
// 0: a7 2a 12 34 ahi %r2,4660
// 4: a7 2e 00 00 chi %r2,0
// 8: c0 64 00 00 00 0b jglh 0x1e
// e: ec 32 12 34 00 d8 ahik %r3,%r2,4660
// 14: a7 3e 00 00 chi %r3,0
// 18: c0 64 ff ff ff fb jglh 0xe
// 1e: a7 2e 00 00 chi %r2,0
// 22: c0 64 ff ff ff f6 jglh 0xe
// 28: a7 2a ed cc ahi %r2,-4660
// 2c: 07 fe br %r14
let golden = vec![
236, 50, 18, 52, 0, 216, 167, 62, 0, 0, 192, 100, 0, 0, 0, 11, 236, 67, 18, 52, 0, 216,
167, 78, 0, 0, 192, 100, 255, 255, 255, 251, 167, 62, 0, 0, 192, 100, 255, 255, 255,
246, 236, 35, 237, 204, 0, 216, 7, 254,
167, 42, 18, 52, 167, 46, 0, 0, 192, 100, 0, 0, 0, 11, 236, 50, 18, 52, 0, 216, 167,
62, 0, 0, 192, 100, 255, 255, 255, 251, 167, 46, 0, 0, 192, 100, 255, 255, 255, 246,
167, 42, 237, 204, 7, 254,
];
assert_eq!(code, &golden[..]);

View File

@@ -298,7 +298,11 @@ impl ABIMachineSpec for X64ABIMachineSpec {
Inst::Args { args }
}
fn gen_ret(_setup_frame: bool, _isa_flags: &x64_settings::Flags, rets: Vec<Reg>) -> Self::I {
fn gen_ret(
_setup_frame: bool,
_isa_flags: &x64_settings::Flags,
rets: Vec<RetPair>,
) -> Self::I {
Inst::ret(rets)
}

View File

@@ -333,7 +333,7 @@
(args VecArgPair))
;; Return.
(Ret (rets VecReg))
(Ret (rets VecRetPair))
;; Jump to a known target: jmp simm32.
(JmpKnown (dst MachLabel))

View File

@@ -526,7 +526,7 @@ impl Inst {
}
}
pub(crate) fn ret(rets: Vec<Reg>) -> Inst {
pub(crate) fn ret(rets: Vec<RetPair>) -> Inst {
Inst::Ret { rets }
}
@@ -2081,8 +2081,8 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
Inst::Ret { rets } => {
// The return value(s) are live-out; we represent this
// with register uses on the return instruction.
for &ret in rets {
collector.reg_use(ret);
for ret in rets.iter() {
collector.reg_fixed_use(ret.vreg, ret.preg);
}
}

View File

@@ -333,31 +333,36 @@ mod test {
.unwrap();
let code = result.buffer.data();
// 00000000 55 push rbp
// 00000001 4889E5 mov rbp,rsp
// 00000004 81C734120000 add edi,0x1234
// 0000000A 85FF test edi,edi
// 0000000C 0F841C000000 jz near 0x2e
// 00000012 4989F8 mov r8,rdi
// 00000015 4889F8 mov rax,rdi
// 00000018 81E834120000 sub eax,0x1234
// 0000001E 4401C0 add eax,r8d
// 00000021 85FF test edi,edi
// 00000023 0F8505000000 jnz near 0x2e
// 00000029 4889EC mov rsp,rbp
// 0000002C 5D pop rbp
// 0000002D C3 ret
// 0000002E 4989F8 mov r8,rdi
// 00000031 4181C034120000 add r8d,0x1234
// 00000038 4585C0 test r8d,r8d
// 0000003B 0F85EDFFFFFF jnz near 0x2e
// 00000041 E9CFFFFFFF jmp 0x15
// To update this comment, write the golden bytes to a file, and run the following
// command on it:
// > objdump -b binary -D <file> -m i386:x86-64 -M intel
//
// 0: 55 push rbp
// 1: 48 89 e5 mov rbp,rsp
// 4: 48 89 fe mov rsi,rdi
// 7: 81 c6 34 12 00 00 add esi,0x1234
// d: 85 f6 test esi,esi
// f: 0f 84 1c 00 00 00 je 0x31
// 15: 49 89 f0 mov r8,rsi
// 18: 48 89 f0 mov rax,rsi
// 1b: 81 e8 34 12 00 00 sub eax,0x1234
// 21: 44 01 c0 add eax,r8d
// 24: 85 f6 test esi,esi
// 26: 0f 85 05 00 00 00 jne 0x31
// 2c: 48 89 ec mov rsp,rbp
// 2f: 5d pop rbp
// 30: c3 ret
// 31: 49 89 f0 mov r8,rsi
// 34: 41 81 c0 34 12 00 00 add r8d,0x1234
// 3b: 45 85 c0 test r8d,r8d
// 3e: 0f 85 ed ff ff ff jne 0x31
// 44: e9 cf ff ff ff jmp 0x18
let golden = vec![
85, 72, 137, 229, 129, 199, 52, 18, 0, 0, 133, 255, 15, 132, 28, 0, 0, 0, 73, 137, 248,
72, 137, 248, 129, 232, 52, 18, 0, 0, 68, 1, 192, 133, 255, 15, 133, 5, 0, 0, 0, 72,
137, 236, 93, 195, 73, 137, 248, 65, 129, 192, 52, 18, 0, 0, 69, 133, 192, 15, 133,
237, 255, 255, 255, 233, 207, 255, 255, 255,
85, 72, 137, 229, 72, 137, 254, 129, 198, 52, 18, 0, 0, 133, 246, 15, 132, 28, 0, 0, 0,
73, 137, 240, 72, 137, 240, 129, 232, 52, 18, 0, 0, 68, 1, 192, 133, 246, 15, 133, 5,
0, 0, 0, 72, 137, 236, 93, 195, 73, 137, 240, 65, 129, 192, 52, 18, 0, 0, 69, 133, 192,
15, 133, 237, 255, 255, 255, 233, 207, 255, 255, 255,
];
assert_eq!(code, &golden[..]);
@@ -433,35 +438,42 @@ mod test {
.unwrap();
let code = result.buffer.data();
// 00000000 55 push rbp
// 00000001 4889E5 mov rbp,rsp
// 00000004 83FF02 cmp edi,byte +0x2
// 00000007 0F8327000000 jnc near 0x34
// 0000000D 448BDF mov r11d,edi
// 00000010 41BA00000000 mov r10d,0x0
// 00000016 4D0F43DA cmovnc r11,r10
// 0000001A 4C8D150B000000 lea r10,[rel 0x2c]
// 00000021 4F635C9A00 movsxd r11,dword [r10+r11*4+0x0]
// 00000026 4D01DA add r10,r11
// 00000029 41FFE2 jmp r10
// 0000002C 120000001C000000 (jumptable data)
// 00000034 B803000000 mov eax,0x3
// 00000039 4889EC mov rsp,rbp
// 0000003C 5D pop rbp
// 0000003D C3 ret
// 0000003E B801000000 mov eax,0x1
// 00000043 4889EC mov rsp,rbp
// 00000046 5D pop rbp
// 00000047 C3 ret
// 00000048 B802000000 mov eax,0x2
// 0000004D 4889EC mov rsp,rbp
// 00000050 5D pop rbp
// 00000051 C3 ret
// To update this comment, write the golden bytes to a file, and run the following
// command on it:
// > objdump -b binary -D <file> -m i386:x86-64 -M intel
//
// 0: 55 push rbp
// 1: 48 89 e5 mov rbp,rsp
// 4: 83 ff 02 cmp edi,0x2
// 7: 0f 83 27 00 00 00 jae 0x34
// d: 44 8b d7 mov r10d,edi
// 10: 41 b9 00 00 00 00 mov r9d,0x0
// 16: 4d 0f 43 d1 cmovae r10,r9
// 1a: 4c 8d 0d 0b 00 00 00 lea r9,[rip+0xb] # 0x2c
// 21: 4f 63 54 91 00 movsxd r10,DWORD PTR [r9+r10*4+0x0]
// 26: 4d 01 d1 add r9,r10
// 29: 41 ff e1 jmp r9
// 2c: 12 00 adc al,BYTE PTR [rax]
// 2e: 00 00 add BYTE PTR [rax],al
// 30: 1c 00 sbb al,0x0
// 32: 00 00 add BYTE PTR [rax],al
// 34: b8 03 00 00 00 mov eax,0x3
// 39: 48 89 ec mov rsp,rbp
// 3c: 5d pop rbp
// 3d: c3 ret
// 3e: b8 01 00 00 00 mov eax,0x1
// 43: 48 89 ec mov rsp,rbp
// 46: 5d pop rbp
// 47: c3 ret
// 48: b8 02 00 00 00 mov eax,0x2
// 4d: 48 89 ec mov rsp,rbp
// 50: 5d pop rbp
// 51: c3 ret
let golden = vec![
85, 72, 137, 229, 131, 255, 2, 15, 131, 39, 0, 0, 0, 68, 139, 223, 65, 186, 0, 0, 0, 0,
77, 15, 67, 218, 76, 141, 21, 11, 0, 0, 0, 79, 99, 92, 154, 0, 77, 1, 218, 65, 255,
226, 18, 0, 0, 0, 28, 0, 0, 0, 184, 3, 0, 0, 0, 72, 137, 236, 93, 195, 184, 1, 0, 0, 0,
85, 72, 137, 229, 131, 255, 2, 15, 131, 39, 0, 0, 0, 68, 139, 215, 65, 185, 0, 0, 0, 0,
77, 15, 67, 209, 76, 141, 13, 11, 0, 0, 0, 79, 99, 84, 145, 0, 77, 1, 209, 65, 255,
225, 18, 0, 0, 0, 28, 0, 0, 0, 184, 3, 0, 0, 0, 72, 137, 236, 93, 195, 184, 1, 0, 0, 0,
72, 137, 236, 93, 195, 184, 2, 0, 0, 0, 72, 137, 236, 93, 195,
];

View File

@@ -136,6 +136,17 @@ pub struct ArgPair {
pub preg: Reg,
}
/// A type used by backends to track return register binding info in the "ret"
/// pseudoinst. The pseudoinst holds a vec of `RetPair` structs.
#[derive(Clone, Debug)]
pub struct RetPair {
/// The vreg that is returned by this pseudionst.
pub vreg: Reg,
/// The preg that the arg is returned through; this constrains the vreg's
/// placement at the pseudoinst.
pub preg: Reg,
}
/// A location for (part of) an argument or return value. These "storage slots"
/// are specified for each register-sized part of an argument.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -421,7 +432,7 @@ pub trait ABIMachineSpec {
fn gen_args(isa_flags: &Self::F, args: Vec<ArgPair>) -> Self::I;
/// Generate a return instruction.
fn gen_ret(setup_frame: bool, isa_flags: &Self::F, rets: Vec<Reg>) -> Self::I;
fn gen_ret(setup_frame: bool, isa_flags: &Self::F, rets: Vec<RetPair>) -> Self::I;
/// Generate an add-with-immediate. Note that even if this uses a scratch
/// register, it must satisfy two requirements:
@@ -1483,8 +1494,10 @@ impl<M: ABIMachineSpec> Callee<M> {
&self,
sigs: &SigSet,
idx: usize,
from_regs: ValueRegs<Writable<Reg>>,
) -> SmallInstVec<M::I> {
from_regs: ValueRegs<Reg>,
vregs: &mut VRegAllocator<M::I>,
) -> (SmallVec<[RetPair; 2]>, SmallInstVec<M::I>) {
let mut reg_pairs = smallvec![];
let mut ret = smallvec![];
let word_bits = M::word_bits() as u8;
match &sigs[self.sig].rets(sigs)[idx] {
@@ -1497,24 +1510,31 @@ impl<M: ABIMachineSpec> Callee<M> {
} => {
let from_bits = ty_bits(ty) as u8;
let ext = M::get_ext_mode(sigs[self.sig].call_conv, extension);
let reg: Writable<Reg> = Writable::from_reg(Reg::from(reg));
match (ext, from_bits) {
(ArgumentExtension::Uext, n) | (ArgumentExtension::Sext, n)
let vreg = match (ext, from_bits) {
(ir::ArgumentExtension::Uext, n)
| (ir::ArgumentExtension::Sext, n)
if n < word_bits =>
{
let signed = ext == ArgumentExtension::Sext;
let signed = ext == ir::ArgumentExtension::Sext;
let dst = writable_value_regs(vregs.alloc(ty).unwrap())
.only_reg()
.unwrap();
ret.push(M::gen_extend(
reg,
from_reg.to_reg(),
signed,
from_bits,
dst, from_reg, signed, from_bits,
/* to_bits = */ word_bits,
));
dst.to_reg()
}
_ => {
ret.push(M::gen_move(reg, from_reg.to_reg(), ty));
// No move needed, regalloc2 will emit it using the constraint
// added by the RetPair.
from_reg
}
};
reg_pairs.push(RetPair {
vreg,
preg: Reg::from(reg),
});
}
&ABIArgSlot::Stack {
offset,
@@ -1533,16 +1553,17 @@ impl<M: ABIMachineSpec> Callee<M> {
let ext = M::get_ext_mode(sigs[self.sig].call_conv, extension);
// Trash the from_reg; it should be its last use.
match (ext, from_bits) {
(ArgumentExtension::Uext, n) | (ArgumentExtension::Sext, n)
(ir::ArgumentExtension::Uext, n)
| (ir::ArgumentExtension::Sext, n)
if n < word_bits =>
{
assert_eq!(M::word_reg_class(), from_reg.to_reg().class());
let signed = ext == ArgumentExtension::Sext;
assert_eq!(M::word_reg_class(), from_reg.class());
let signed = ext == ir::ArgumentExtension::Sext;
let dst = writable_value_regs(vregs.alloc(ty).unwrap())
.only_reg()
.unwrap();
ret.push(M::gen_extend(
Writable::from_reg(from_reg.to_reg()),
from_reg.to_reg(),
signed,
from_bits,
dst, from_reg, signed, from_bits,
/* to_bits = */ word_bits,
));
// Store the extended version.
@@ -1553,21 +1574,21 @@ impl<M: ABIMachineSpec> Callee<M> {
ret.push(M::gen_store_base_offset(
self.ret_area_ptr.unwrap().to_reg(),
off,
from_reg.to_reg(),
from_reg,
ty,
));
}
}
}
}
&ABIArg::StructArg { .. } => {
ABIArg::StructArg { .. } => {
panic!("StructArg in return position is unsupported");
}
&ABIArg::ImplicitPtrArg { .. } => {
ABIArg::ImplicitPtrArg { .. } => {
panic!("ImplicitPtrArg in return position is unsupported");
}
}
ret
(reg_pairs, ret)
}
/// Generate any setup instruction needed to save values to the
@@ -1594,22 +1615,7 @@ impl<M: ABIMachineSpec> Callee<M> {
}
/// Generate a return instruction.
pub fn gen_ret(&self, sigs: &SigSet) -> M::I {
let mut rets = vec![];
for ret in sigs[self.sig].rets(sigs) {
match ret {
ABIArg::Slots { slots, .. } => {
for slot in slots {
match slot {
ABIArgSlot::Reg { reg, .. } => rets.push(Reg::from(*reg)),
_ => {}
}
}
}
_ => {}
}
}
pub fn gen_ret(&self, rets: Vec<RetPair>) -> M::I {
M::gen_ret(self.setup_frame, &self.isa_flags, rets)
}

View File

@@ -7,8 +7,9 @@ use std::cell::Cell;
use target_lexicon::Triple;
pub use super::MachLabel;
use super::RetPair;
pub use crate::ir::{
condcodes, condcodes::CondCode, dynamic_to_fixed, ArgumentExtension, Constant,
condcodes, condcodes::CondCode, dynamic_to_fixed, ArgumentExtension, ArgumentPurpose, Constant,
DynamicStackSlot, ExternalName, FuncRef, GlobalValue, Immediate, SigRef, StackSlot,
};
pub use crate::isa::unwind::UnwindInst;
@@ -23,7 +24,7 @@ pub type ValueSlice = (ValueList, usize);
pub type ValueArray2 = [Value; 2];
pub type ValueArray3 = [Value; 3];
pub type WritableReg = Writable<Reg>;
pub type VecReg = Vec<Reg>;
pub type VecRetPair = Vec<RetPair>;
pub type VecMask = Vec<u8>;
pub type ValueRegs = crate::machinst::ValueRegs<Reg>;
pub type WritableValueRegs = crate::machinst::ValueRegs<WritableReg>;
@@ -422,10 +423,6 @@ macro_rules! isle_lower_prelude_methods {
))
}
fn retval(&mut self, i: usize) -> WritableValueRegs {
self.lower_ctx.retval(i)
}
fn only_writable_reg(&mut self, regs: WritableValueRegs) -> Option<WritableReg> {
regs.only_reg()
}
@@ -590,6 +587,17 @@ macro_rules! isle_lower_prelude_methods {
fn floatcc_inverse(&mut self, cc: &FloatCC) -> FloatCC {
cc.inverse()
}
/// Generate the return instruction.
fn gen_return(&mut self, (list, off): ValueSlice) {
let rets = (off..list.len(&self.lower_ctx.dfg().value_lists))
.map(|ix| {
let val = list.get(ix, &self.lower_ctx.dfg().value_lists).unwrap();
self.put_in_regs(val)
})
.collect();
self.lower_ctx.gen_return(rets);
}
};
}

View File

@@ -158,8 +158,8 @@ pub struct Lower<'func, I: VCodeInst> {
/// Mapping from `Value` (SSA value in IR) to virtual register.
value_regs: SecondaryMap<Value, ValueRegs<Reg>>,
/// Return-value vregs.
retval_regs: Vec<ValueRegs<Reg>>,
/// sret registers, if needed.
sret_reg: Option<ValueRegs<Reg>>,
/// Instruction colors at block exits. From this map, we can recover all
/// instruction colors by scanning backward from the block end and
@@ -370,12 +370,13 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
}
}
// Assign vreg(s) to each return value.
let mut retval_regs = vec![];
// Make a sret register, if one is needed.
let mut sret_reg = None;
for ret in &vcode.abi().signature().returns.clone() {
let regs = vregs.alloc(ret.value_type)?;
retval_regs.push(regs);
trace!("retval gets regs {:?}", regs);
if ret.purpose == ArgumentPurpose::StructReturn {
assert!(sret_reg.is_none());
sret_reg = Some(vregs.alloc(ret.value_type)?);
}
}
// Compute instruction colors, find constant instructions, and find instructions with
@@ -414,7 +415,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
vcode,
vregs,
value_regs,
retval_regs,
sret_reg,
block_end_colors,
side_effect_inst_entry_colors,
inst_constants,
@@ -576,15 +577,15 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
let ty = self.abi().signature().params[i].value_type;
// The ABI implementation must have ensured that a StructReturn
// arg is present in the return values.
let struct_ret_idx = self
assert!(self
.abi()
.signature()
.returns
.iter()
.position(|ret| ret.purpose == ArgumentPurpose::StructReturn)
.expect("StructReturn return value not present!");
.is_some());
self.emit(I::gen_move(
Writable::from_reg(self.retval_regs[struct_ret_idx].regs()[0]),
Writable::from_reg(self.sret_reg.unwrap().regs()[0]),
regs.regs()[0].to_reg(),
ty,
));
@@ -611,21 +612,36 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
}
}
fn gen_retval_setup(&mut self) {
let retval_regs = self.retval_regs.clone();
for (i, regs) in retval_regs.into_iter().enumerate() {
let regs = writable_value_regs(regs);
for insn in self
.vcode
.abi()
.gen_copy_regs_to_retval(self.sigs(), i, regs)
.into_iter()
{
/// Generate the return instruction.
pub fn gen_return(&mut self, rets: Vec<ValueRegs<Reg>>) {
let mut out_rets = vec![];
let mut rets = rets.into_iter();
for (i, ret) in self
.abi()
.signature()
.returns
.clone()
.into_iter()
.enumerate()
{
let regs = if ret.purpose == ArgumentPurpose::StructReturn {
self.sret_reg.unwrap().clone()
} else {
rets.next().unwrap()
};
let (regs, insns) = self.vcode.abi().gen_copy_regs_to_retval(
self.vcode.sigs(),
i,
regs,
&mut self.vregs,
);
out_rets.extend(regs);
for insn in insns {
self.emit(insn);
}
}
let inst = self.vcode.abi().gen_ret(self.sigs());
self.emit(inst);
// Hack: generate a virtual instruction that uses vmctx in
// order to keep it alive for the duration of the function,
@@ -636,6 +652,9 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
self.emit(I::gen_dummy_use(vmctx_reg));
}
}
let inst = self.abi().gen_ret(out_rets);
self.emit(inst);
}
/// Has this instruction been sunk to a use-site (i.e., away from its
@@ -720,10 +739,6 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
trace!("lowering: inst {}: {:?}", inst, self.f.dfg[inst]);
backend.lower(self, inst)?;
}
if data.opcode().is_return() {
// Return: handle specially, using ABI-appropriate sequence.
self.gen_retval_setup();
}
let loc = self.srcloc(inst);
self.finish_ir_inst(loc);
@@ -1026,14 +1041,6 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
pub fn abi_mut(&mut self) -> &mut Callee<I::ABIMachineSpec> {
self.vcode.abi_mut()
}
/// Get the (virtual) register that receives the return value. A return
/// instruction should lower into a sequence that fills this register. (Why
/// not allow the backend to specify its own result register for the return?
/// Because there may be multiple return points.)
pub fn retval(&self, idx: usize) -> ValueRegs<Writable<Reg>> {
writable_value_regs(self.retval_regs[idx])
}
}
/// Instruction input/output queries.

View File

@@ -158,6 +158,7 @@
(type BoxExternalName (primitive BoxExternalName))
(type RelocDistance (primitive RelocDistance))
(type VecArgPair extern (enum))
(type VecRetPair extern (enum))
;;;; Helper Clif Extractors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -614,10 +615,6 @@
;;;; Helpers for generating returns ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The (writable) register(s) that will contain the n'th return value.
(decl retval (usize) WritableValueRegs)
(extern constructor retval retval)
;; Extractor to check for the special case that a `WritableValueRegs`
;; contains only a single register.
(decl only_writable_reg (WritableReg) WritableValueRegs)
@@ -725,31 +722,14 @@
(decl gen_move (Type WritableReg Reg) MInst)
(extern constructor gen_move gen_move)
;; Copy a return value to a set of registers.
(decl copy_to_regs (WritableValueRegs Value) Unit)
(rule (copy_to_regs dsts val @ (value_type ty))
(let ((srcs ValueRegs (put_in_regs val)))
(copy_to_regs_range ty (value_regs_range srcs) dsts srcs)))
;; Helper for `copy_to_regs` that uses a range to index into the reg/value
;; vectors. Fails for the empty range.
(decl copy_to_regs_range (Type Range WritableValueRegs ValueRegs) Unit)
(rule (copy_to_regs_range ty (range_empty) dsts srcs)
(unit))
(rule (copy_to_regs_range ty (range_unwrap head tail) dsts srcs)
(let ((dst WritableReg (writable_regs_get dsts head))
(src Reg (value_regs_get srcs head))
(_ Unit (emit (gen_move ty dst src))))
(copy_to_regs_range ty tail dsts srcs)))
;; Generate a return instruction
(decl lower_return (Range ValueSlice) InstOutput)
(rule (lower_return (range_empty) _) (output_none))
(rule (lower_return (range_unwrap head tail) args)
(let ((_ Unit (copy_to_regs (retval head) (value_slice_get args head))))
(lower_return tail args)))
(rule (lower_return _ vals)
(let ((_ Unit (gen_return vals)))
(output_none)))
(decl gen_return (ValueSlice) Unit)
(extern constructor gen_return gen_return)
;;;; Automatic conversions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;