Aarch64: fix narrow integer-register extension with Baldrdash ABI.

In the Baldrdash (SpiderMonkey) embedding, we must take care to
zero-extend all function arguments to callees in integer registers when
the types are narrower than 64 bits. This is because, unlike the native
SysV ABI, the Baldrdash ABI expects high bits to be cleared. Not doing
so leads to difficult-to-trace errors where high bits falsely tag an
int32 as e.g. an object pointer, leading to potential security issues.
This commit is contained in:
Chris Fallin
2020-07-29 14:28:07 -07:00
parent 8fd92093a4
commit 1fbdf169b5
5 changed files with 258 additions and 71 deletions

View File

@@ -113,9 +113,9 @@ use log::{debug, trace};
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
enum ABIArg { enum ABIArg {
/// In a real register. /// In a real register.
Reg(RealReg, ir::Type), Reg(RealReg, ir::Type, ir::ArgumentExtension),
/// Arguments only: on stack, at given offset from SP at entry. /// Arguments only: on stack, at given offset from SP at entry.
Stack(i64, ir::Type), Stack(i64, ir::Type, ir::ArgumentExtension),
} }
/// AArch64 ABI information shared between body (callee) and caller. /// AArch64 ABI information shared between body (callee) and caller.
@@ -187,6 +187,7 @@ fn try_fill_baldrdash_reg(call_conv: isa::CallConv, param: &ir::AbiParam) -> Opt
Some(ABIArg::Reg( Some(ABIArg::Reg(
xreg(BALDRDASH_TLS_REG).to_real_reg(), xreg(BALDRDASH_TLS_REG).to_real_reg(),
ir::types::I64, ir::types::I64,
param.extension,
)) ))
} }
&ir::ArgumentPurpose::SignatureId => { &ir::ArgumentPurpose::SignatureId => {
@@ -194,6 +195,7 @@ fn try_fill_baldrdash_reg(call_conv: isa::CallConv, param: &ir::AbiParam) -> Opt
Some(ABIArg::Reg( Some(ABIArg::Reg(
xreg(BALDRDASH_SIG_REG).to_real_reg(), xreg(BALDRDASH_SIG_REG).to_real_reg(),
ir::types::I64, ir::types::I64,
param.extension,
)) ))
} }
_ => None, _ => None,
@@ -279,7 +281,11 @@ fn compute_arg_locs(
} else { } else {
vreg(*next_reg) vreg(*next_reg)
}; };
ret.push(ABIArg::Reg(reg.to_real_reg(), param.value_type)); ret.push(ABIArg::Reg(
reg.to_real_reg(),
param.value_type,
param.extension,
));
*next_reg += 1; *next_reg += 1;
} else { } else {
// Compute size. Every arg takes a minimum slot of 8 bytes. (16-byte // Compute size. Every arg takes a minimum slot of 8 bytes. (16-byte
@@ -289,7 +295,11 @@ fn compute_arg_locs(
// Align. // Align.
debug_assert!(size.is_power_of_two()); debug_assert!(size.is_power_of_two());
next_stack = (next_stack + size - 1) & !(size - 1); next_stack = (next_stack + size - 1) & !(size - 1);
ret.push(ABIArg::Stack(next_stack as i64, param.value_type)); ret.push(ABIArg::Stack(
next_stack as i64,
param.value_type,
param.extension,
));
next_stack += size; next_stack += size;
} }
} }
@@ -301,9 +311,17 @@ fn compute_arg_locs(
let extra_arg = if add_ret_area_ptr { let extra_arg = if add_ret_area_ptr {
debug_assert!(args_or_rets == ArgsOrRets::Args); debug_assert!(args_or_rets == ArgsOrRets::Args);
if next_xreg < max_reg_vals { if next_xreg < max_reg_vals {
ret.push(ABIArg::Reg(xreg(next_xreg).to_real_reg(), I64)); ret.push(ABIArg::Reg(
xreg(next_xreg).to_real_reg(),
I64,
ir::ArgumentExtension::None,
));
} else { } else {
ret.push(ABIArg::Stack(next_stack as i64, I64)); ret.push(ABIArg::Stack(
next_stack as i64,
I64,
ir::ArgumentExtension::None,
));
next_stack += 8; next_stack += 8;
} }
Some(ret.len() - 1) Some(ret.len() - 1)
@@ -491,7 +509,7 @@ fn get_special_purpose_param_register(
) -> Option<Reg> { ) -> Option<Reg> {
let idx = f.signature.special_param_index(purpose)?; let idx = f.signature.special_param_index(purpose)?;
match abi.args[idx] { match abi.args[idx] {
ABIArg::Reg(reg, _) => Some(reg.to_reg()), ABIArg::Reg(reg, ..) => Some(reg.to_reg()),
ABIArg::Stack(..) => None, ABIArg::Stack(..) => None,
} }
} }
@@ -866,7 +884,7 @@ impl ABIBody for AArch64ABIBody {
fn liveins(&self) -> Set<RealReg> { fn liveins(&self) -> Set<RealReg> {
let mut set: Set<RealReg> = Set::empty(); let mut set: Set<RealReg> = Set::empty();
for &arg in &self.sig.args { for &arg in &self.sig.args {
if let ABIArg::Reg(r, _) = arg { if let ABIArg::Reg(r, ..) = arg {
set.insert(r); set.insert(r);
} }
} }
@@ -876,7 +894,7 @@ impl ABIBody for AArch64ABIBody {
fn liveouts(&self) -> Set<RealReg> { fn liveouts(&self) -> Set<RealReg> {
let mut set: Set<RealReg> = Set::empty(); let mut set: Set<RealReg> = Set::empty();
for &ret in &self.sig.rets { for &ret in &self.sig.rets {
if let ABIArg::Reg(r, _) = ret { if let ABIArg::Reg(r, ..) = ret {
set.insert(r); set.insert(r);
} }
} }
@@ -897,8 +915,10 @@ impl ABIBody for AArch64ABIBody {
fn gen_copy_arg_to_reg(&self, idx: usize, into_reg: Writable<Reg>) -> Inst { fn gen_copy_arg_to_reg(&self, idx: usize, into_reg: Writable<Reg>) -> Inst {
match &self.sig.args[idx] { match &self.sig.args[idx] {
&ABIArg::Reg(r, ty) => Inst::gen_move(into_reg, r.to_reg(), ty), // Extension mode doesn't matter (we're copying out, not in; we
&ABIArg::Stack(off, ty) => load_stack( // ignore high bits by convention).
&ABIArg::Reg(r, ty, _) => Inst::gen_move(into_reg, r.to_reg(), ty),
&ABIArg::Stack(off, ty, _) => load_stack(
MemArg::FPOffset(self.fp_to_arg_offset() + off, ty), MemArg::FPOffset(self.fp_to_arg_offset() + off, ty),
into_reg, into_reg,
ty, ty,
@@ -921,15 +941,10 @@ impl ABIBody for AArch64ABIBody {
} }
} }
fn gen_copy_reg_to_retval( fn gen_copy_reg_to_retval(&self, idx: usize, from_reg: Writable<Reg>) -> Vec<Inst> {
&self,
idx: usize,
from_reg: Writable<Reg>,
ext: ArgumentExtension,
) -> Vec<Inst> {
let mut ret = Vec::new(); let mut ret = Vec::new();
match &self.sig.rets[idx] { match &self.sig.rets[idx] {
&ABIArg::Reg(r, ty) => { &ABIArg::Reg(r, ty, ext) => {
let from_bits = ty_bits(ty) as u8; let from_bits = ty_bits(ty) as u8;
let dest_reg = Writable::from_reg(r.to_reg()); let dest_reg = Writable::from_reg(r.to_reg());
match (ext, from_bits) { match (ext, from_bits) {
@@ -954,7 +969,7 @@ impl ABIBody for AArch64ABIBody {
_ => ret.push(Inst::gen_move(dest_reg, from_reg.to_reg(), ty)), _ => ret.push(Inst::gen_move(dest_reg, from_reg.to_reg(), ty)),
}; };
} }
&ABIArg::Stack(off, ty) => { &ABIArg::Stack(off, ty, ext) => {
let from_bits = ty_bits(ty) as u8; let from_bits = ty_bits(ty) as u8;
// Trash the from_reg; it should be its last use. // Trash the from_reg; it should be its last use.
match (ext, from_bits) { match (ext, from_bits) {
@@ -1364,7 +1379,7 @@ fn abisig_to_uses_and_defs(sig: &ABISig) -> (Vec<Reg>, Vec<Writable<Reg>>) {
let mut uses = Vec::new(); let mut uses = Vec::new();
for arg in &sig.args { for arg in &sig.args {
match arg { match arg {
&ABIArg::Reg(reg, _) => uses.push(reg.to_reg()), &ABIArg::Reg(reg, ..) => uses.push(reg.to_reg()),
_ => {} _ => {}
} }
} }
@@ -1373,7 +1388,7 @@ fn abisig_to_uses_and_defs(sig: &ABISig) -> (Vec<Reg>, Vec<Writable<Reg>>) {
let mut defs = get_caller_saves(sig.call_conv); let mut defs = get_caller_saves(sig.call_conv);
for ret in &sig.rets { for ret in &sig.rets {
match ret { match ret {
&ABIArg::Reg(reg, _) => defs.push(Writable::from_reg(reg.to_reg())), &ABIArg::Reg(reg, ..) => defs.push(Writable::from_reg(reg.to_reg())),
_ => {} _ => {}
} }
} }
@@ -1469,12 +1484,49 @@ impl ABICall for AArch64ABICall {
from_reg: Reg, from_reg: Reg,
) { ) {
match &self.sig.args[idx] { match &self.sig.args[idx] {
&ABIArg::Reg(reg, ty) => ctx.emit(Inst::gen_move( &ABIArg::Reg(reg, ty, ext)
Writable::from_reg(reg.to_reg()), if ext != ir::ArgumentExtension::None && ty_bits(ty) < 64 =>
from_reg, {
ty, assert_eq!(RegClass::I64, reg.get_class());
)), let signed = match ext {
&ABIArg::Stack(off, ty) => { ir::ArgumentExtension::Uext => false,
ir::ArgumentExtension::Sext => true,
_ => unreachable!(),
};
ctx.emit(Inst::Extend {
rd: Writable::from_reg(reg.to_reg()),
rn: from_reg,
signed,
from_bits: ty_bits(ty) as u8,
to_bits: 64,
});
}
&ABIArg::Reg(reg, ty, _) => {
ctx.emit(Inst::gen_move(
Writable::from_reg(reg.to_reg()),
from_reg,
ty,
));
}
&ABIArg::Stack(off, ty, ext) => {
if ext != ir::ArgumentExtension::None && ty_bits(ty) < 64 {
assert_eq!(RegClass::I64, from_reg.get_class());
let signed = match ext {
ir::ArgumentExtension::Uext => false,
ir::ArgumentExtension::Sext => true,
_ => unreachable!(),
};
// Extend in place in the source register. Our convention is to
// treat high bits as undefined for values in registers, so this
// is safe, even for an argument that is nominally read-only.
ctx.emit(Inst::Extend {
rd: Writable::from_reg(from_reg),
rn: from_reg,
signed,
from_bits: ty_bits(ty) as u8,
to_bits: 64,
});
}
ctx.emit(store_stack(MemArg::SPOffset(off, ty), from_reg, ty)) ctx.emit(store_stack(MemArg::SPOffset(off, ty), from_reg, ty))
} }
} }
@@ -1487,8 +1539,10 @@ impl ABICall for AArch64ABICall {
into_reg: Writable<Reg>, into_reg: Writable<Reg>,
) { ) {
match &self.sig.rets[idx] { match &self.sig.rets[idx] {
&ABIArg::Reg(reg, ty) => ctx.emit(Inst::gen_move(into_reg, reg.to_reg(), ty)), // Extension mode doesn't matter because we're copying out, not in,
&ABIArg::Stack(off, ty) => { // and we ignore high bits in our own registers by convention.
&ABIArg::Reg(reg, ty, _) => ctx.emit(Inst::gen_move(into_reg, reg.to_reg(), ty)),
&ABIArg::Stack(off, ty, _) => {
let ret_area_base = self.sig.stack_arg_space; let ret_area_base = self.sig.stack_arg_space;
ctx.emit(load_stack( ctx.emit(load_stack(
MemArg::SPOffset(off + ret_area_base, ty), MemArg::SPOffset(off + ret_area_base, ty),

View File

@@ -23,8 +23,8 @@ static STACK_ARG_RET_SIZE_LIMIT: u64 = 128 * 1024 * 1024;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum ABIArg { enum ABIArg {
Reg(RealReg, ir::Type), Reg(RealReg, ir::Type, ir::ArgumentExtension),
Stack(i64, ir::Type), Stack(i64, ir::Type, ir::ArgumentExtension),
} }
/// X64 ABI information shared between body (callee) and caller. /// X64 ABI information shared between body (callee) and caller.
@@ -302,7 +302,7 @@ impl ABIBody for X64ABIBody {
fn liveins(&self) -> Set<RealReg> { fn liveins(&self) -> Set<RealReg> {
let mut set: Set<RealReg> = Set::empty(); let mut set: Set<RealReg> = Set::empty();
for arg in &self.sig.args { for arg in &self.sig.args {
if let &ABIArg::Reg(r, _) = arg { if let &ABIArg::Reg(r, ..) = arg {
set.insert(r); set.insert(r);
} }
} }
@@ -312,7 +312,7 @@ impl ABIBody for X64ABIBody {
fn liveouts(&self) -> Set<RealReg> { fn liveouts(&self) -> Set<RealReg> {
let mut set: Set<RealReg> = Set::empty(); let mut set: Set<RealReg> = Set::empty();
for ret in &self.sig.rets { for ret in &self.sig.rets {
if let &ABIArg::Reg(r, _) = ret { if let &ABIArg::Reg(r, ..) = ret {
set.insert(r); set.insert(r);
} }
} }
@@ -321,8 +321,8 @@ impl ABIBody for X64ABIBody {
fn gen_copy_arg_to_reg(&self, idx: usize, to_reg: Writable<Reg>) -> Inst { fn gen_copy_arg_to_reg(&self, idx: usize, to_reg: Writable<Reg>) -> Inst {
match &self.sig.args[idx] { match &self.sig.args[idx] {
ABIArg::Reg(from_reg, ty) => Inst::gen_move(to_reg, from_reg.to_reg(), *ty), ABIArg::Reg(from_reg, ty, _) => Inst::gen_move(to_reg, from_reg.to_reg(), *ty),
&ABIArg::Stack(off, ty) => { &ABIArg::Stack(off, ty, _) => {
assert!( assert!(
self.fp_to_arg_offset() + off <= u32::max_value() as i64, self.fp_to_arg_offset() + off <= u32::max_value() as i64,
"large offset nyi" "large offset nyi"
@@ -351,15 +351,10 @@ impl ABIBody for X64ABIBody {
} }
} }
fn gen_copy_reg_to_retval( fn gen_copy_reg_to_retval(&self, idx: usize, from_reg: Writable<Reg>) -> Vec<Inst> {
&self,
idx: usize,
from_reg: Writable<Reg>,
ext: ArgumentExtension,
) -> Vec<Inst> {
let mut ret = Vec::new(); let mut ret = Vec::new();
match &self.sig.rets[idx] { match &self.sig.rets[idx] {
&ABIArg::Reg(r, ty) => { &ABIArg::Reg(r, ty, ext) => {
let from_bits = ty.bits() as u8; let from_bits = ty.bits() as u8;
let ext_mode = match from_bits { let ext_mode = match from_bits {
1 | 8 => Some(ExtMode::BQ), 1 | 8 => Some(ExtMode::BQ),
@@ -391,7 +386,7 @@ impl ABIBody for X64ABIBody {
}; };
} }
&ABIArg::Stack(off, ty) => { &ABIArg::Stack(off, ty, ext) => {
let from_bits = ty.bits() as u8; let from_bits = ty.bits() as u8;
let ext_mode = match from_bits { let ext_mode = match from_bits {
1 | 8 => Some(ExtMode::BQ), 1 | 8 => Some(ExtMode::BQ),
@@ -758,7 +753,7 @@ fn abisig_to_uses_and_defs(sig: &ABISig) -> (Vec<Reg>, Vec<Writable<Reg>>) {
let mut uses = Vec::new(); let mut uses = Vec::new();
for arg in &sig.args { for arg in &sig.args {
match arg { match arg {
&ABIArg::Reg(reg, _) => uses.push(reg.to_reg()), &ABIArg::Reg(reg, ..) => uses.push(reg.to_reg()),
_ => {} _ => {}
} }
} }
@@ -767,7 +762,7 @@ fn abisig_to_uses_and_defs(sig: &ABISig) -> (Vec<Reg>, Vec<Writable<Reg>>) {
let mut defs = get_caller_saves(sig.call_conv); let mut defs = get_caller_saves(sig.call_conv);
for ret in &sig.rets { for ret in &sig.rets {
match ret { match ret {
&ABIArg::Reg(reg, _) => defs.push(Writable::from_reg(reg.to_reg())), &ABIArg::Reg(reg, ..) => defs.push(Writable::from_reg(reg.to_reg())),
_ => {} _ => {}
} }
} }
@@ -781,11 +776,19 @@ fn try_fill_baldrdash_reg(call_conv: CallConv, param: &ir::AbiParam) -> Option<A
match &param.purpose { match &param.purpose {
&ir::ArgumentPurpose::VMContext => { &ir::ArgumentPurpose::VMContext => {
// This is SpiderMonkey's `WasmTlsReg`. // This is SpiderMonkey's `WasmTlsReg`.
Some(ABIArg::Reg(regs::r14().to_real_reg(), ir::types::I64)) Some(ABIArg::Reg(
regs::r14().to_real_reg(),
ir::types::I64,
param.extension,
))
} }
&ir::ArgumentPurpose::SignatureId => { &ir::ArgumentPurpose::SignatureId => {
// This is SpiderMonkey's `WasmTableCallSigReg`. // This is SpiderMonkey's `WasmTableCallSigReg`.
Some(ABIArg::Reg(regs::r10().to_real_reg(), ir::types::I64)) Some(ABIArg::Reg(
regs::r10().to_real_reg(),
ir::types::I64,
param.extension,
))
} }
_ => None, _ => None,
} }
@@ -872,7 +875,11 @@ fn compute_arg_locs(
assert!(intreg); assert!(intreg);
ret.push(param); ret.push(param);
} else if let Some(reg) = candidate { } else if let Some(reg) = candidate {
ret.push(ABIArg::Reg(reg.to_real_reg(), param.value_type)); ret.push(ABIArg::Reg(
reg.to_real_reg(),
param.value_type,
param.extension,
));
*next_reg += 1; *next_reg += 1;
} else { } else {
// Compute size. Every arg takes a minimum slot of 8 bytes. (16-byte // Compute size. Every arg takes a minimum slot of 8 bytes. (16-byte
@@ -882,7 +889,11 @@ fn compute_arg_locs(
// Align. // Align.
debug_assert!(size.is_power_of_two()); debug_assert!(size.is_power_of_two());
next_stack = (next_stack + size - 1) & !(size - 1); next_stack = (next_stack + size - 1) & !(size - 1);
ret.push(ABIArg::Stack(next_stack as i64, param.value_type)); ret.push(ABIArg::Stack(
next_stack as i64,
param.value_type,
param.extension,
));
next_stack += size; next_stack += size;
} }
} }
@@ -894,9 +905,17 @@ fn compute_arg_locs(
let extra_arg = if add_ret_area_ptr { let extra_arg = if add_ret_area_ptr {
debug_assert!(args_or_rets == ArgsOrRets::Args); debug_assert!(args_or_rets == ArgsOrRets::Args);
if let Some(reg) = get_intreg_for_arg_systemv(&call_conv, next_gpr) { if let Some(reg) = get_intreg_for_arg_systemv(&call_conv, next_gpr) {
ret.push(ABIArg::Reg(reg.to_real_reg(), ir::types::I64)); ret.push(ABIArg::Reg(
reg.to_real_reg(),
ir::types::I64,
ir::ArgumentExtension::None,
));
} else { } else {
ret.push(ABIArg::Stack(next_stack as i64, ir::types::I64)); ret.push(ABIArg::Stack(
next_stack as i64,
ir::types::I64,
ir::ArgumentExtension::None,
));
next_stack += 8; next_stack += 8;
} }
Some(ret.len() - 1) Some(ret.len() - 1)
@@ -1125,12 +1144,74 @@ impl ABICall for X64ABICall {
from_reg: Reg, from_reg: Reg,
) { ) {
match &self.sig.args[idx] { match &self.sig.args[idx] {
&ABIArg::Reg(reg, ty) => ctx.emit(Inst::gen_move( &ABIArg::Reg(reg, ty, ext) if ext != ir::ArgumentExtension::None && ty.bits() < 64 => {
assert_eq!(RegClass::I64, reg.get_class());
let dest_reg = Writable::from_reg(reg.to_reg());
let ext_mode = match ty.bits() {
1 | 8 => ExtMode::BQ,
16 => ExtMode::WQ,
32 => ExtMode::LQ,
_ => unreachable!(),
};
match ext {
ir::ArgumentExtension::Uext => {
ctx.emit(Inst::movzx_rm_r(
ext_mode,
RegMem::reg(from_reg),
dest_reg,
/* infallible load */ None,
));
}
ir::ArgumentExtension::Sext => {
ctx.emit(Inst::movsx_rm_r(
ext_mode,
RegMem::reg(from_reg),
dest_reg,
/* infallible load */ None,
));
}
_ => unreachable!(),
};
}
&ABIArg::Reg(reg, ty, _) => ctx.emit(Inst::gen_move(
Writable::from_reg(reg.to_reg()), Writable::from_reg(reg.to_reg()),
from_reg, from_reg,
ty, ty,
)), )),
&ABIArg::Stack(off, ty) => { &ABIArg::Stack(off, ty, ext) => {
if ext != ir::ArgumentExtension::None && ty.bits() < 64 {
assert_eq!(RegClass::I64, from_reg.get_class());
let dest_reg = Writable::from_reg(from_reg);
let ext_mode = match ty.bits() {
1 | 8 => ExtMode::BQ,
16 => ExtMode::WQ,
32 => ExtMode::LQ,
_ => unreachable!(),
};
// Extend in place in the source register. Our convention is to
// treat high bits as undefined for values in registers, so this
// is safe, even for an argument that is nominally read-only.
match ext {
ir::ArgumentExtension::Uext => {
ctx.emit(Inst::movzx_rm_r(
ext_mode,
RegMem::reg(from_reg),
dest_reg,
/* infallible load */ None,
));
}
ir::ArgumentExtension::Sext => {
ctx.emit(Inst::movsx_rm_r(
ext_mode,
RegMem::reg(from_reg),
dest_reg,
/* infallible load */ None,
));
}
_ => unreachable!(),
};
}
debug_assert!(off <= u32::max_value() as i64); debug_assert!(off <= u32::max_value() as i64);
debug_assert!(off >= 0); debug_assert!(off >= 0);
ctx.emit(store_stack( ctx.emit(store_stack(
@@ -1149,8 +1230,8 @@ impl ABICall for X64ABICall {
into_reg: Writable<Reg>, into_reg: Writable<Reg>,
) { ) {
match &self.sig.rets[idx] { match &self.sig.rets[idx] {
&ABIArg::Reg(reg, ty) => ctx.emit(Inst::gen_move(into_reg, reg.to_reg(), ty)), &ABIArg::Reg(reg, ty, _) => ctx.emit(Inst::gen_move(into_reg, reg.to_reg(), ty)),
&ABIArg::Stack(off, ty) => { &ABIArg::Stack(off, ty, _) => {
let ret_area_base = self.sig.stack_arg_space; let ret_area_base = self.sig.stack_arg_space;
let sp_offset = off + ret_area_base; let sp_offset = off + ret_area_base;
// TODO handle offsets bigger than u32::max // TODO handle offsets bigger than u32::max

View File

@@ -1,7 +1,7 @@
//! ABI definitions. //! ABI definitions.
use crate::binemit::Stackmap; use crate::binemit::Stackmap;
use crate::ir::{ArgumentExtension, StackSlot}; use crate::ir::StackSlot;
use crate::machinst::*; use crate::machinst::*;
use crate::settings; use crate::settings;
@@ -52,12 +52,7 @@ pub trait ABIBody {
fn gen_retval_area_setup(&self) -> Option<Self::I>; fn gen_retval_area_setup(&self) -> Option<Self::I>;
/// Generate an instruction which copies a source register to a return value slot. /// Generate an instruction which copies a source register to a return value slot.
fn gen_copy_reg_to_retval( fn gen_copy_reg_to_retval(&self, idx: usize, from_reg: Writable<Reg>) -> Vec<Self::I>;
&self,
idx: usize,
from_reg: Writable<Reg>,
ext: ArgumentExtension,
) -> Vec<Self::I>;
/// Generate a return instruction. /// Generate a return instruction.
fn gen_ret(&self) -> Self::I; fn gen_ret(&self) -> Self::I;

View File

@@ -8,9 +8,8 @@ use crate::inst_predicates::{has_side_effect_or_load, is_constant_64bit};
use crate::ir::instructions::BranchInfo; use crate::ir::instructions::BranchInfo;
use crate::ir::types::I64; use crate::ir::types::I64;
use crate::ir::{ use crate::ir::{
ArgumentExtension, ArgumentPurpose, Block, Constant, ConstantData, ExternalName, Function, ArgumentPurpose, Block, Constant, ConstantData, ExternalName, Function, GlobalValueData, Inst,
GlobalValueData, Inst, InstructionData, MemFlags, Opcode, Signature, SourceLoc, Type, Value, InstructionData, MemFlags, Opcode, Signature, SourceLoc, Type, Value, ValueDef,
ValueDef,
}; };
use crate::machinst::{ use crate::machinst::{
ABIBody, BlockIndex, BlockLoweringOrder, LoweredBlock, MachLabel, VCode, VCodeBuilder, ABIBody, BlockIndex, BlockLoweringOrder, LoweredBlock, MachLabel, VCode, VCodeBuilder,
@@ -232,7 +231,7 @@ pub struct Lower<'func, I: VCodeInst> {
value_regs: SecondaryMap<Value, Reg>, value_regs: SecondaryMap<Value, Reg>,
/// Return-value vregs. /// Return-value vregs.
retval_regs: Vec<(Reg, ArgumentExtension)>, retval_regs: Vec<Reg>,
/// Instruction colors. /// Instruction colors.
inst_colors: SecondaryMap<Inst, InstColor>, inst_colors: SecondaryMap<Inst, InstColor>,
@@ -354,7 +353,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
next_vreg += 1; next_vreg += 1;
let regclass = I::rc_for_type(ret.value_type)?; let regclass = I::rc_for_type(ret.value_type)?;
let vreg = Reg::new_virtual(regclass, v); let vreg = Reg::new_virtual(regclass, v);
retval_regs.push((vreg, ret.extension)); retval_regs.push(vreg);
vcode.set_vreg_type(vreg.as_virtual_reg().unwrap(), ret.value_type); vcode.set_vreg_type(vreg.as_virtual_reg().unwrap(), ret.value_type);
} }
@@ -427,9 +426,9 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
fn gen_retval_setup(&mut self, gen_ret_inst: GenerateReturn) { fn gen_retval_setup(&mut self, gen_ret_inst: GenerateReturn) {
let retval_regs = self.retval_regs.clone(); let retval_regs = self.retval_regs.clone();
for (i, (reg, ext)) in retval_regs.into_iter().enumerate() { for (i, reg) in retval_regs.into_iter().enumerate() {
let reg = Writable::from_reg(reg); let reg = Writable::from_reg(reg);
let insns = self.vcode.abi().gen_copy_reg_to_retval(i, reg, ext); let insns = self.vcode.abi().gen_copy_reg_to_retval(i, reg);
for insn in insns { for insn in insns {
self.emit(insn); self.emit(insn);
} }
@@ -844,7 +843,7 @@ impl<'func, I: VCodeInst> LowerCtx for Lower<'func, I> {
} }
fn retval(&self, idx: usize) -> Writable<Reg> { fn retval(&self, idx: usize) -> Writable<Reg> {
Writable::from_reg(self.retval_regs[idx].0) Writable::from_reg(self.retval_regs[idx])
} }
fn get_vm_context(&self) -> Option<Reg> { fn get_vm_context(&self) -> Option<Reg> {

View File

@@ -1,7 +1,7 @@
test compile test compile
target aarch64 target aarch64
function %f(i64) -> i64 { function %f1(i64) -> i64 {
fn0 = %g(i64) -> i64 fn0 = %g(i64) -> i64
block0(v0: i64): block0(v0: i64):
@@ -16,3 +16,61 @@ block0(v0: i64):
; nextln: mov sp, fp ; nextln: mov sp, fp
; nextln: ldp fp, lr, [sp], #16 ; nextln: ldp fp, lr, [sp], #16
; nextln: ret ; nextln: ret
function %f2(i32) -> i64 {
fn0 = %g(i32 uext) -> i64
block0(v0: i32):
v1 = call fn0(v0)
return v1
}
; check: stp fp, lr, [sp, #-16]!
; nextln: mov fp, sp
; nextln: mov w0, w0
; nextln: ldr x16, 8 ; b 12 ; data
; nextln: blr x16
; nextln: mov sp, fp
; nextln: ldp fp, lr, [sp], #16
; nextln: ret
function %f3(i32) -> i32 uext {
block0(v0: i32):
return v0
}
; check: stp fp, lr, [sp, #-16]!
; nextln: mov fp, sp
; nextln: mov w0, w0
; nextln: mov sp, fp
; nextln: ldp fp, lr, [sp], #16
; nextln: ret
function %f4(i32) -> i64 {
fn0 = %g(i32 sext) -> i64
block0(v0: i32):
v1 = call fn0(v0)
return v1
}
; check: stp fp, lr, [sp, #-16]!
; nextln: mov fp, sp
; nextln: sxtw x0, w0
; nextln: ldr x16, 8 ; b 12 ; data
; nextln: blr x16
; nextln: mov sp, fp
; nextln: ldp fp, lr, [sp], #16
; nextln: ret
function %f3(i32) -> i32 sext {
block0(v0: i32):
return v0
}
; check: stp fp, lr, [sp, #-16]!
; nextln: mov fp, sp
; nextln: sxtw x0, w0
; nextln: mov sp, fp
; nextln: ldp fp, lr, [sp], #16
; nextln: ret