Reimplement I32Ctz, I64Clz and I64Ctz without LZCNT or TZCNT.

This commit is contained in:
Nick Lewycky 2019-10-15 13:42:05 -07:00
parent 3e854c4a3b
commit 99f7499a05
3 changed files with 163 additions and 28 deletions

View File

@ -2393,12 +2393,57 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
_ => {}
};
}
Operator::I32Ctz => Self::emit_xcnt_i32(
a,
&mut self.machine,
&mut self.value_stack,
Assembler::emit_tzcnt,
),
Operator::I32Ctz => {
let loc =
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
let src = match loc {
Location::Imm32(_) | Location::Memory(_, _) => {
let tmp = self.machine.acquire_temp_gpr().unwrap();
a.emit_mov(Size::S32, loc, Location::GPR(tmp));
tmp
}
Location::GPR(reg) => reg,
_ => unreachable!(),
};
let ret = self.machine.acquire_locations(
a,
&[(WpType::I32, MachineValue::WasmStack(self.value_stack.len()))],
false,
)[0];
self.value_stack.push(ret);
let dst = match ret {
Location::Memory(_, _) => self.machine.acquire_temp_gpr().unwrap(),
Location::GPR(reg) => reg,
_ => unreachable!(),
};
let zero_path = a.get_label();
let end = a.get_label();
a.emit_test_gpr_64(src);
a.emit_jmp(Condition::Equal, zero_path);
a.emit_bsf(Size::S32, Location::GPR(src), Location::GPR(dst));
a.emit_jmp(Condition::None, end);
a.emit_label(zero_path);
a.emit_mov(Size::S32, Location::Imm32(32), Location::GPR(dst));
a.emit_label(end);
match loc {
Location::Imm32(_) | Location::Memory(_, _) => {
self.machine.release_temp_gpr(src);
}
_ => {}
};
match ret {
Location::Memory(_, _) => {
a.emit_mov(Size::S32, Location::GPR(dst), ret);
self.machine.release_temp_gpr(dst);
}
_ => {}
};
}
Operator::I32Popcnt => Self::emit_xcnt_i32(
a,
&mut self.machine,
@ -2678,18 +2723,109 @@ impl FunctionCodeGenerator<CodegenError> for X64FunctionCode {
Condition::Equal,
Location::Imm64(0),
),
Operator::I64Clz => Self::emit_xcnt_i64(
a,
&mut self.machine,
&mut self.value_stack,
Assembler::emit_lzcnt,
),
Operator::I64Ctz => Self::emit_xcnt_i64(
a,
&mut self.machine,
&mut self.value_stack,
Assembler::emit_tzcnt,
),
Operator::I64Clz => {
let loc =
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
let src = match loc {
Location::Imm64(_) | Location::Imm32(_) | Location::Memory(_, _) => {
let tmp = self.machine.acquire_temp_gpr().unwrap();
a.emit_mov(Size::S64, loc, Location::GPR(tmp));
tmp
}
Location::GPR(reg) => reg,
_ => unreachable!(),
};
let ret = self.machine.acquire_locations(
a,
&[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))],
false,
)[0];
self.value_stack.push(ret);
let dst = match ret {
Location::Memory(_, _) => self.machine.acquire_temp_gpr().unwrap(),
Location::GPR(reg) => reg,
_ => unreachable!(),
};
let zero_path = a.get_label();
let end = a.get_label();
a.emit_test_gpr_64(src);
a.emit_jmp(Condition::Equal, zero_path);
a.emit_bsr(Size::S64, Location::GPR(src), Location::GPR(dst));
a.emit_xor(Size::S64, Location::Imm32(63), Location::GPR(dst));
a.emit_jmp(Condition::None, end);
a.emit_label(zero_path);
a.emit_mov(Size::S64, Location::Imm32(64), Location::GPR(dst));
a.emit_label(end);
match loc {
Location::Imm64(_) | Location::Imm32(_) | Location::Memory(_, _) => {
self.machine.release_temp_gpr(src);
}
_ => {}
};
match ret {
Location::Memory(_, _) => {
a.emit_mov(Size::S64, Location::GPR(dst), ret);
self.machine.release_temp_gpr(dst);
}
_ => {}
};
}
Operator::I64Ctz => {
let loc =
get_location_released(a, &mut self.machine, self.value_stack.pop().unwrap());
let src = match loc {
Location::Imm64(_) | Location::Imm32(_) | Location::Memory(_, _) => {
let tmp = self.machine.acquire_temp_gpr().unwrap();
a.emit_mov(Size::S64, loc, Location::GPR(tmp));
tmp
}
Location::GPR(reg) => reg,
_ => unreachable!(),
};
let ret = self.machine.acquire_locations(
a,
&[(WpType::I64, MachineValue::WasmStack(self.value_stack.len()))],
false,
)[0];
self.value_stack.push(ret);
let dst = match ret {
Location::Memory(_, _) => self.machine.acquire_temp_gpr().unwrap(),
Location::GPR(reg) => reg,
_ => unreachable!(),
};
let zero_path = a.get_label();
let end = a.get_label();
a.emit_test_gpr_64(src);
a.emit_jmp(Condition::Equal, zero_path);
a.emit_bsf(Size::S64, Location::GPR(src), Location::GPR(dst));
a.emit_jmp(Condition::None, end);
a.emit_label(zero_path);
a.emit_mov(Size::S64, Location::Imm32(64), Location::GPR(dst));
a.emit_label(end);
match loc {
Location::Imm64(_) | Location::Imm32(_) | Location::Memory(_, _) => {
self.machine.release_temp_gpr(src);
}
_ => {}
};
match ret {
Location::Memory(_, _) => {
a.emit_mov(Size::S64, Location::GPR(dst), ret);
self.machine.release_temp_gpr(dst);
}
_ => {}
};
}
Operator::I64Popcnt => Self::emit_xcnt_i64(
a,
&mut self.machine,

View File

@ -91,6 +91,7 @@ pub trait Emitter {
fn emit_and(&mut self, sz: Size, src: Location, dst: Location);
fn emit_or(&mut self, sz: Size, src: Location, dst: Location);
fn emit_bsr(&mut self, sz: Size, src: Location, dst: Location);
fn emit_bsf(&mut self, sz: Size, src: Location, dst: Location);
fn emit_lzcnt(&mut self, sz: Size, src: Location, dst: Location);
fn emit_tzcnt(&mut self, sz: Size, src: Location, dst: Location);
fn emit_popcnt(&mut self, sz: Size, src: Location, dst: Location);
@ -763,6 +764,14 @@ impl Emitter for Assembler {
});
}
fn emit_bsf(&mut self, sz: Size, src: Location, dst: Location) {
binop_gpr_gpr!(bsf, self, sz, src, dst, {
binop_mem_gpr!(bsf, self, sz, src, dst, {
panic!("singlepass can't emit BSF {:?} {:?} {:?}", sz, src, dst)
})
});
}
fn emit_lzcnt(&mut self, sz: Size, src: Location, dst: Location) {
binop_gpr_gpr!(lzcnt, self, sz, src, dst, {
binop_mem_gpr!(lzcnt, self, sz, src, dst, {

View File

@ -902,7 +902,6 @@ singlepass:fail:i32.wast:99 # AssertTrap - expected trap, got Runtime:Error unkn
singlepass:fail:i32.wast:100 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:i32.wast:120 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:i32.wast:121 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:i32.wast:252 # AssertReturn - result I32(0) ("0x0") does not match expected I32(32) ("0x20")
singlepass:fail:i64.wast:62 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:i64.wast:63 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:i64.wast:64 # AssertTrap - expected trap, got Runtime:Error unknown error
@ -912,15 +911,6 @@ singlepass:fail:i64.wast:99 # AssertTrap - expected trap, got Runtime:Error unkn
singlepass:fail:i64.wast:100 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:i64.wast:120 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:i64.wast:121 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:i64.wast:242 # AssertReturn - result I64(63) ("0x3f") does not match expected I64(0) ("0x0")
singlepass:fail:i64.wast:243 # AssertReturn - result I64(0) ("0x0") does not match expected I64(64) ("0x40")
singlepass:fail:i64.wast:244 # AssertReturn - result I64(15) ("0xf") does not match expected I64(48) ("0x30")
singlepass:fail:i64.wast:245 # AssertReturn - result I64(7) ("0x7") does not match expected I64(56) ("0x38")
singlepass:fail:i64.wast:246 # AssertReturn - result I64(63) ("0x3f") does not match expected I64(0) ("0x0")
singlepass:fail:i64.wast:247 # AssertReturn - result I64(0) ("0x0") does not match expected I64(63) ("0x3f")
singlepass:fail:i64.wast:248 # AssertReturn - result I64(1) ("0x1") does not match expected I64(62) ("0x3e")
singlepass:fail:i64.wast:249 # AssertReturn - result I64(62) ("0x3e") does not match expected I64(1) ("0x1")
singlepass:fail:i64.wast:252 # AssertReturn - result I64(0) ("0x0") does not match expected I64(64) ("0x40")
singlepass:fail:if.wast:440 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:imports.wast:283 # AssertTrap - expected trap, got Runtime:Error unknown error
singlepass:fail:imports.wast:286 # AssertTrap - expected trap, got Runtime:Error unknown error