Implement rem in backend - not every backend will act like x86

This commit is contained in:
Jef
2019-04-24 12:32:17 +02:00
parent ea1ec9491e
commit 745d9ae162
2 changed files with 86 additions and 93 deletions

View File

@@ -371,6 +371,16 @@ pub enum CCLoc {
Stack(i32),
}
impl CCLoc {
fn try_from(other: ValueLocation) -> Option<Self> {
match other {
ValueLocation::Reg(reg) => Some(CCLoc::Reg(reg)),
ValueLocation::Stack(offset) => Some(CCLoc::Stack(offset)),
_ => None,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum CondCode {
CF0,
@@ -670,27 +680,27 @@ impl StackDepth {
}
macro_rules! int_div {
($full_div_s:ident, $full_div_u:ident, $div_u:ident, $div_s:ident, $rem_u:ident, $rem_s:ident, $imm_fn:ident, $signed_ty:ty, $unsigned_ty:ty) => {
($full_div_s:ident, $full_div_u:ident, $div_u:ident, $div_s:ident, $rem_u:ident, $rem_s:ident, $imm_fn:ident, $signed_ty:ty, $unsigned_ty:ty, $reg_ty:tt, $pointer_ty:tt) => {
// TODO: Fast div using mul for constant divisor? It looks like LLVM doesn't do that for us when
// emitting Wasm.
pub fn $div_u(&mut self) {
let divisor = self.pop();
let quotient = self.pop();
let dividend = self.pop();
if let (Some(quotient), Some(divisor)) = (quotient.$imm_fn(), divisor.$imm_fn()) {
if let (Some(dividend), Some(divisor)) = (dividend.$imm_fn(), divisor.$imm_fn()) {
if divisor == 0 {
self.trap();
self.push(ValueLocation::Immediate((0 as $unsigned_ty).into()));
} else {
self.push(ValueLocation::Immediate(
<$unsigned_ty>::wrapping_div(quotient as _, divisor as _).into(),
<$unsigned_ty>::wrapping_div(dividend as _, divisor as _).into(),
));
}
return;
}
let (div, rem, mut saved) = self.$full_div_u(divisor, quotient);
let (div, rem, mut saved) = self.$full_div_u(divisor, dividend);
self.free_value(rem);
@@ -719,22 +729,22 @@ macro_rules! int_div {
// emitting Wasm.
pub fn $div_s(&mut self) {
let divisor = self.pop();
let quotient = self.pop();
let dividend = self.pop();
if let (Some(quotient), Some(divisor)) = (quotient.$imm_fn(), divisor.$imm_fn()) {
if let (Some(dividend), Some(divisor)) = (dividend.$imm_fn(), divisor.$imm_fn()) {
if divisor == 0 {
self.trap();
self.push(ValueLocation::Immediate((0 as $signed_ty).into()));
} else {
self.push(ValueLocation::Immediate(
<$signed_ty>::wrapping_div(quotient, divisor).into(),
<$signed_ty>::wrapping_div(dividend, divisor).into(),
));
}
return;
}
let (div, rem, mut saved) = self.$full_div_s(divisor, quotient);
let (div, rem, mut saved) = self.$full_div_s(divisor, dividend);
self.free_value(rem);
@@ -761,21 +771,21 @@ macro_rules! int_div {
pub fn $rem_u(&mut self) {
let divisor = self.pop();
let quotient = self.pop();
let dividend = self.pop();
if let (Some(quotient), Some(divisor)) = (quotient.$imm_fn(), divisor.$imm_fn()) {
if let (Some(dividend), Some(divisor)) = (dividend.$imm_fn(), divisor.$imm_fn()) {
if divisor == 0 {
self.trap();
self.push(ValueLocation::Immediate((0 as $unsigned_ty).into()));
} else {
self.push(ValueLocation::Immediate(
(quotient as $unsigned_ty % divisor as $unsigned_ty).into(),
(dividend as $unsigned_ty % divisor as $unsigned_ty).into(),
));
}
return;
}
let (div, rem, mut saved) = self.$full_div_u(divisor, quotient);
let (div, rem, mut saved) = self.$full_div_u(divisor, dividend);
self.free_value(div);
@@ -802,19 +812,54 @@ macro_rules! int_div {
pub fn $rem_s(&mut self) {
let divisor = self.pop();
let quotient = self.pop();
let dividend = self.pop();
if let (Some(quotient), Some(divisor)) = (quotient.$imm_fn(), divisor.$imm_fn()) {
if let (Some(dividend), Some(divisor)) = (dividend.$imm_fn(), divisor.$imm_fn()) {
if divisor == 0 {
self.trap();
self.push(ValueLocation::Immediate((0 as $signed_ty).into()));
} else {
self.push(ValueLocation::Immediate((quotient % divisor).into()));
self.push(ValueLocation::Immediate((dividend % divisor).into()));
}
return;
}
let (div, rem, mut saved) = self.$full_div_s(divisor, quotient);
let is_neg1 = self.create_label();
let gen_neg1_case = match divisor {
ValueLocation::Immediate(_) => {
if divisor.$imm_fn().unwrap() == -1 {
self.push(ValueLocation::Immediate((-1 as $signed_ty).into()));
return;
}
false
}
ValueLocation::Reg(_) => {
let reg = self.into_reg(GPRType::Rq, divisor).unwrap();
dynasm!(self.asm
; cmp $reg_ty(reg.rq().unwrap()), -1
; je =>is_neg1.0
);
true
}
ValueLocation::Stack(offset) => {
let offset = self.adjusted_offset(offset);
dynasm!(self.asm
; cmp $pointer_ty [rsp + offset], -1
; je =>is_neg1.0
);
true
}
ValueLocation::Cond(_) => {
// `cc` can never be `-1`, only `0` and `1`
false
}
};
let (div, rem, mut saved) = self.$full_div_s(divisor, dividend);
self.free_value(div);
@@ -836,6 +881,21 @@ macro_rules! int_div {
self.cleanup_gprs(saved);
if gen_neg1_case {
let ret = self.create_label();
dynasm!(self.asm
; jmp =>ret.0
);
self.define_label(is_neg1);
self.copy_value(
ValueLocation::Immediate((0 as $signed_ty).into()),
CCLoc::try_from(rem).expect("Programmer error")
);
self.define_label(ret);
}
self.push(rem);
}
}
@@ -4033,7 +4093,9 @@ impl<'this, M: ModuleContext> Context<'this, M> {
i32_rem_s,
imm_i32,
i32,
u32
u32,
Rd,
DWORD
);
int_div!(
i64_full_div_s,
@@ -4044,7 +4106,9 @@ impl<'this, M: ModuleContext> Context<'this, M> {
i64_rem_s,
imm_i64,
i64,
u64
u64,
Rq,
QWORD
);
/// Returned divisor is guaranteed not to be `RAX`

View File

@@ -1946,44 +1946,8 @@ where
// Unlike Wasm, our `rem_s` instruction _does_ trap on `-1`. Instead
// of handling this complexity in the backend, we handle it here
// (where it's way easier to debug).
WasmOperator::I32RemS => {
let id = self.next_id();
WasmOperator::I32RemS => smallvec![Operator::Rem(sint::I32),],
let then = (id, NameTag::Header);
let else_ = (id, NameTag::Else);
let end = (id, NameTag::End);
let mut end_params = self.block_params();
end_params.pop();
end_params.pop();
end_params.push(I32);
smallvec![
Operator::block(self.block_params(), then),
Operator::block(self.block_params(), else_),
Operator::end(end_params, end),
Operator::Pick(0),
Operator::Const((-1i32).into()),
Operator::Ne(I32),
Operator::BrIf {
then: BrTarget::Label(then).into(),
else_: BrTarget::Label(else_).into()
},
Operator::Label(then),
Operator::Rem(sint::I32),
Operator::Br {
target: BrTarget::Label(end).into()
},
Operator::Label(else_),
Operator::Drop(0..=1),
Operator::Const(0i32.into()),
Operator::Br {
target: BrTarget::Label(end).into()
},
Operator::Label(end),
]
}
WasmOperator::I32RemU => smallvec![Operator::Rem(sint::U32),],
WasmOperator::I32And => smallvec![Operator::And(Size::_32)],
WasmOperator::I32Or => smallvec![Operator::Or(Size::_32)],
@@ -2001,44 +1965,8 @@ where
WasmOperator::I64Mul => smallvec![Operator::Mul(I64)],
WasmOperator::I64DivS => smallvec![Operator::Div(SI64)],
WasmOperator::I64DivU => smallvec![Operator::Div(SU64)],
WasmOperator::I64RemS => {
let id = self.next_id();
WasmOperator::I64RemS => smallvec![Operator::Rem(sint::I64),],
let then = (id, NameTag::Header);
let else_ = (id, NameTag::Else);
let end = (id, NameTag::End);
let mut end_params = self.block_params();
end_params.pop();
end_params.pop();
end_params.push(I64);
smallvec![
Operator::block(self.block_params(), then),
Operator::block(self.block_params(), else_),
Operator::end(end_params, end),
Operator::Pick(0),
Operator::Const((-1i64).into()),
Operator::Ne(I64),
Operator::BrIf {
then: BrTarget::Label(then).into(),
else_: BrTarget::Label(else_).into()
},
Operator::Label(then),
Operator::Rem(sint::I64),
Operator::Br {
target: BrTarget::Label(end).into()
},
Operator::Label(else_),
Operator::Drop(0..=1),
Operator::Const(0i64.into()),
Operator::Br {
target: BrTarget::Label(end).into()
},
Operator::Label(end),
]
}
WasmOperator::I64RemU => smallvec![Operator::Rem(sint::U64)],
WasmOperator::I64And => smallvec![Operator::And(Size::_64)],
WasmOperator::I64Or => smallvec![Operator::Or(Size::_64)],
@@ -2174,3 +2102,4 @@ where
}))
}
}