Adding the first instructions, mfspr/mtspr.

This commit is contained in:
Ben Vanik
2013-05-24 01:37:02 -07:00
parent 71bd3c799f
commit 605d66ecf6
6 changed files with 590 additions and 634 deletions

View File

@@ -571,7 +571,7 @@ void X64Emitter::GenerateBasicBlock(FunctionBlock* block) {
continue;
}
typedef int (*InstrEmitter)(X64Emitter& g, Compiler& c, InstrData& i);
typedef int (*InstrEmitter)(X64Emitter& g, X86Compiler& c, InstrData& i);
InstrEmitter emit = (InstrEmitter)i.type->emit;
if (!i.type->emit || emit(*this, compiler_, i)) {
// This printf is handy for sort/uniquify to find instructions.
@@ -599,142 +599,6 @@ void X64Emitter::GenerateBasicBlock(FunctionBlock* block) {
// TODO(benvanik): finish up BB
}
// jit_value_t X64Emitter::get_int32(int32_t value) {
// return jit_value_create_nint_constant(fn_, jit_type_int, value);
// }
// jit_value_t X64Emitter::get_uint32(uint32_t value) {
// return jit_value_create_nint_constant(fn_, jit_type_uint, value);
// }
// jit_value_t X64Emitter::get_int64(int64_t value) {
// return jit_value_create_nint_constant(fn_, jit_type_nint, value);
// }
// jit_value_t X64Emitter::get_uint64(uint64_t value) {
// return jit_value_create_nint_constant(fn_, jit_type_nuint, value);
// }
// jit_value_t X64Emitter::make_signed(jit_value_t value) {
// jit_type_t source_type = jit_value_get_type(value);
// source_type = jit_type_normalize(source_type);
// jit_type_t signed_source_type = source_type;
// switch (jit_type_get_kind(source_type)) {
// case JIT_TYPE_UBYTE: signed_source_type = jit_type_sbyte; break;
// case JIT_TYPE_USHORT: signed_source_type = jit_type_short; break;
// case JIT_TYPE_UINT: signed_source_type = jit_type_int; break;
// case JIT_TYPE_NUINT: signed_source_type = jit_type_nint; break;
// case JIT_TYPE_ULONG: signed_source_type = jit_type_long; break;
// }
// if (signed_source_type != source_type) {
// value = jit_insn_convert(fn_, value, signed_source_type, 0);
// }
// return value;
// }
// jit_value_t X64Emitter::make_unsigned(jit_value_t value) {
// jit_type_t source_type = jit_value_get_type(value);
// source_type = jit_type_normalize(source_type);
// jit_type_t unsigned_source_type = source_type;
// switch (jit_type_get_kind(source_type)) {
// case JIT_TYPE_SBYTE: unsigned_source_type = jit_type_ubyte; break;
// case JIT_TYPE_SHORT: unsigned_source_type = jit_type_ushort; break;
// case JIT_TYPE_INT: unsigned_source_type = jit_type_uint; break;
// case JIT_TYPE_NINT: unsigned_source_type = jit_type_nuint; break;
// case JIT_TYPE_LONG: unsigned_source_type = jit_type_ulong; break;
// }
// if (unsigned_source_type != source_type) {
// value = jit_insn_convert(fn_, value, unsigned_source_type, 0);
// }
// return value;
// }
// jit_value_t X64Emitter::sign_extend(jit_value_t value,
// jit_type_t target_type) {
// // TODO(benvanik): better conversion checking.
// // X64 follows the C rules, which is that the source type indicates whether
// // sign extension occurs.
// // For example, int -> ulong is sign extended,
// // uint -> ulong is zero extended.
// // We convert to the same type with the expected sign and then use the built
// // in convert, only if needed.
// // No-op if the same types.
// jit_type_t source_type = jit_value_get_type(value);
// source_type = jit_type_normalize(source_type);
// target_type = jit_type_normalize(target_type);
// if (source_type == target_type) {
// return value;
// }
// // If just a sign change, simple conversion.
// if (jit_type_get_size(source_type) == jit_type_get_size(target_type)) {
// return jit_insn_convert(fn_, value, target_type, 0);
// }
// // Otherwise, need to convert to signed of the current type then extend.
// value = make_signed(value);
// return jit_insn_convert(fn_, value, target_type, 0);
// }
// jit_value_t X64Emitter::zero_extend(jit_value_t value,
// jit_type_t target_type) {
// // See the comment in ::sign_extend for more information.
// // No-op if the same types.
// jit_type_t source_type = jit_value_get_type(value);
// source_type = jit_type_normalize(source_type);
// target_type = jit_type_normalize(target_type);
// if (source_type == target_type) {
// return value;
// }
// // If just a sign change, simple conversion.
// if (jit_type_get_size(source_type) == jit_type_get_size(target_type)) {
// return jit_insn_convert(fn_, value, target_type, 0);
// }
// // Otherwise, need to convert to signed of the current type then extend.
// value = make_unsigned(value);
// return jit_insn_convert(fn_, value, target_type, 0);
// }
// jit_value_t X64Emitter::trunc_to_sbyte(jit_value_t value) {
// jit_type_t source_type = jit_value_get_type(value);
// source_type = jit_type_normalize(source_type);
// if (source_type == jit_type_sbyte) {
// return value;
// }
// return jit_insn_convert(fn_, value, jit_type_sbyte, 0);
// }
// jit_value_t X64Emitter::trunc_to_ubyte(jit_value_t value) {
// jit_type_t source_type = jit_value_get_type(value);
// source_type = jit_type_normalize(source_type);
// if (source_type == jit_type_ubyte) {
// return value;
// }
// return jit_insn_convert(fn_, value, jit_type_ubyte, 0);
// }
// jit_value_t X64Emitter::trunc_to_short(jit_value_t value) {
// jit_type_t source_type = jit_value_get_type(value);
// source_type = jit_type_normalize(source_type);
// if (source_type == jit_type_sbyte) {
// return value;
// }
// return jit_insn_convert(fn_, value, jit_type_short, 0);
// }
// jit_value_t X64Emitter::trunc_to_int(jit_value_t value) {
// jit_type_t source_type = jit_value_get_type(value);
// source_type = jit_type_normalize(source_type);
// if (source_type == jit_type_sbyte) {
// return value;
// }
// return jit_insn_convert(fn_, value, jit_type_int, 0);
// }
// int X64Emitter::branch_to_block(uint32_t address) {
// std::map<uint32_t, jit_label_t>::iterator it = bbs_.find(address);
// return jit_insn_branch(fn_, &it->second);
@@ -1027,21 +891,6 @@ void X64Emitter::TraceBranch(uint32_t cia) {
// return 0;
// }
// jit_value_t X64Emitter::LoadStateValue(size_t offset, jit_type_t type,
// const char* name) {
// // Load from ppc_state[offset].
// // TODO(benvanik): tag with debug info?
// return jit_insn_load_relative(
// fn_, jit_value_get_param(fn_, 0), offset, type);
// }
// void X64Emitter::StoreStateValue(size_t offset, jit_type_t type,
// jit_value_t value) {
// // Store to ppc_state[offset].
// jit_insn_store_relative(
// fn_, jit_value_get_param(fn_, 0), offset, value);
// }
void X64Emitter::SetupLocals() {
X86Compiler& c = compiler_;
@@ -1142,7 +991,9 @@ void X64Emitter::FillRegisters() {
}
// (cr >> 28 - n * 4) & 0xF
c.mov(cr_tmp, cr);
c.shr(cr_tmp, imm(28 - n * 4));
if (n < 4) {
c.shr(cr_tmp, imm(28 - n * 4));
}
c.and_(cr_tmp, imm(0xF));
c.mov(cr_n, cr_tmp);
}
@@ -1211,10 +1062,15 @@ void X64Emitter::SpillRegisters() {
}
if (cr_tmp.getId() == kInvalidValue) {
cr_tmp = c.newGpVar();
if (FLAGS_annotate_disassembly) {
c.comment("Spilling CR");
}
}
// cr |= (cr_n << n * 4)
c.mov(cr_tmp, cr_n);
c.shl(cr_tmp, imm(n * 4));
if (n) {
c.shl(cr_tmp, imm(n * 4));
}
if (cr.getId() == kInvalidValue) {
cr = c.newGpVar();
c.mov(cr, cr_tmp);
@@ -1223,9 +1079,6 @@ void X64Emitter::SpillRegisters() {
}
}
if (cr.getId() != kInvalidValue) {
if (FLAGS_annotate_disassembly) {
c.comment("Spilling CR");
}
c.mov(ptr(c.getGpArg(0), offsetof(xe_ppc_state_t, cr)),
cr);
}
@@ -1253,189 +1106,193 @@ void X64Emitter::SpillRegisters() {
}
}
// jit_value_t X64Emitter::xer_value() {
// XEASSERTNOTNULL(locals_.xer);
// return jit_insn_load(fn_, locals_.xer);
// }
GpVar& X64Emitter::xer_value() {
X86Compiler& c = compiler_;
XEASSERT(locals_.xer.getId() != kInvalidValue);
return locals_.xer;
}
// void X64Emitter::update_xer_value(jit_value_t value) {
// XEASSERTNOTNULL(locals_.xer);
void X64Emitter::update_xer_value(GpVar& value) {
X86Compiler& c = compiler_;
XEASSERT(locals_.xer.getId() != kInvalidValue);
c.mov(locals_.xer, zero_extend(value, 8));
}
// // Extend to 64bits if needed.
// value = zero_extend(value, jit_type_nuint);
// jit_insn_store(fn_, locals_.xer, value);
// }
#if 0
void X64Emitter::update_xer_with_overflow(GpVar& value) {
X86Compiler& c = compiler_;
XEASSERT(locals_.xer.getId() != kInvalidValue);
// void X64Emitter::update_xer_with_overflow(jit_value_t value) {
// XEASSERTNOTNULL(locals_.xer);
// Expects a i1 indicating overflow.
// Trust the caller that if it's larger than that it's already truncated.
value = zero_extend(value, 8);
// // Expects a i1 indicating overflow.
// // Trust the caller that if it's larger than that it's already truncated.
// value = zero_extend(value, jit_type_nuint);
GpVar& xer = xer_value();
xer = jit_insn_and(fn_, xer, get_uint64(0xFFFFFFFFBFFFFFFF)); // clear bit 30
xer = jit_insn_or(fn_, xer, jit_insn_shl(fn_, value, get_uint32(31)));
xer = jit_insn_or(fn_, xer, jit_insn_shl(fn_, value, get_uint32(30)));
jit_insn_store(fn_, locals_.xer, value);
}
#endif
// jit_value_t xer = xer_value();
// xer = jit_insn_and(fn_, xer, get_uint64(0xFFFFFFFFBFFFFFFF)); // clear bit 30
// xer = jit_insn_or(fn_, xer, jit_insn_shl(fn_, value, get_uint32(31)));
// xer = jit_insn_or(fn_, xer, jit_insn_shl(fn_, value, get_uint32(30)));
// jit_insn_store(fn_, locals_.xer, value);
// }
#if 0
void X64Emitter::update_xer_with_carry(GpVar& value) {
X86Compiler& c = compiler_;
XEASSERT(locals_.xer.getId() != kInvalidValue);
// void X64Emitter::update_xer_with_carry(jit_value_t value) {
// XEASSERTNOTNULL(locals_.xer);
// Expects a i1 indicating carry.
// Trust the caller that if it's larger than that it's already truncated.
value = zero_extend(value, jit_type_nuint);
// // Expects a i1 indicating carry.
// // Trust the caller that if it's larger than that it's already truncated.
// value = zero_extend(value, jit_type_nuint);
GpVar& xer = xer_value();
xer = jit_insn_and(fn_, xer, get_uint64(0xFFFFFFFFDFFFFFFF)); // clear bit 29
xer = jit_insn_or(fn_, xer, jit_insn_shl(fn_, value, get_uint32(29)));
jit_insn_store(fn_, locals_.xer, value);
}
#endif
// jit_value_t xer = xer_value();
// xer = jit_insn_and(fn_, xer, get_uint64(0xFFFFFFFFDFFFFFFF)); // clear bit 29
// xer = jit_insn_or(fn_, xer, jit_insn_shl(fn_, value, get_uint32(29)));
// jit_insn_store(fn_, locals_.xer, value);
// }
#if 0
void X64Emitter::update_xer_with_overflow_and_carry(GpVar& value) {
X86Compiler& c = compiler_;
XEASSERT(locals_.xer.getId() != kInvalidValue);
// void X64Emitter::update_xer_with_overflow_and_carry(jit_value_t value) {
// XEASSERTNOTNULL(locals_.xer);
// Expects a i1 indicating overflow.
// Trust the caller that if it's larger than that it's already truncated.
value = zero_extend(value, jit_type_nuint);
// // Expects a i1 indicating overflow.
// // Trust the caller that if it's larger than that it's already truncated.
// value = zero_extend(value, jit_type_nuint);
// This is effectively an update_xer_with_overflow followed by an
// update_xer_with_carry, but since the logic is largely the same share it.
GpVar& xer = xer_value();
// clear bit 30 & 29
xer = jit_insn_and(fn_, xer, get_uint64(0xFFFFFFFF9FFFFFFF));
xer = jit_insn_or(fn_, xer, jit_insn_shl(fn_, value, get_uint32(31)));
xer = jit_insn_or(fn_, xer, jit_insn_shl(fn_, value, get_uint32(30)));
xer = jit_insn_or(fn_, xer, jit_insn_shl(fn_, value, get_uint32(29)));
jit_insn_store(fn_, locals_.xer, value);
}
#endif
// // This is effectively an update_xer_with_overflow followed by an
// // update_xer_with_carry, but since the logic is largely the same share it.
// jit_value_t xer = xer_value();
// // clear bit 30 & 29
// xer = jit_insn_and(fn_, xer, get_uint64(0xFFFFFFFF9FFFFFFF));
// xer = jit_insn_or(fn_, xer, jit_insn_shl(fn_, value, get_uint32(31)));
// xer = jit_insn_or(fn_, xer, jit_insn_shl(fn_, value, get_uint32(30)));
// xer = jit_insn_or(fn_, xer, jit_insn_shl(fn_, value, get_uint32(29)));
// jit_insn_store(fn_, locals_.xer, value);
// }
GpVar& X64Emitter::lr_value() {
X86Compiler& c = compiler_;
XEASSERT(locals_.lr.getId() != kInvalidValue);
return locals_.lr;
}
// jit_value_t X64Emitter::lr_value() {
// XEASSERTNOTNULL(locals_.lr);
// return jit_insn_load(fn_, locals_.lr);
// }
void X64Emitter::update_lr_value(GpVar& value) {
X86Compiler& c = compiler_;
XEASSERT(locals_.lr.getId() != kInvalidValue);
c.mov(locals_.lr, zero_extend(value, 8));
}
// void X64Emitter::update_lr_value(jit_value_t value) {
// XEASSERTNOTNULL(locals_.lr);
GpVar& X64Emitter::ctr_value() {
X86Compiler& c = compiler_;
XEASSERT(locals_.ctr.getId() != kInvalidValue);
return locals_.ctr;
}
// // Extend to 64bits if needed.
// value = zero_extend(value, jit_type_nuint);
// jit_insn_store(fn_, locals_.lr, value);
// }
void X64Emitter::update_ctr_value(GpVar& value) {
X86Compiler& c = compiler_;
XEASSERT(locals_.ctr.getId() != kInvalidValue);
c.mov(locals_.ctr, zero_extend(value, 8));
}
// jit_value_t X64Emitter::ctr_value() {
// XEASSERTNOTNULL(locals_.ctr);
// return jit_insn_load(fn_, locals_.ctr);
// }
GpVar& X64Emitter::cr_value(uint32_t n) {
X86Compiler& c = compiler_;
XEASSERT(n >= 0 && n < 8);
XEASSERT(locals_.cr[n].getId() != kInvalidValue);
return locals_.cr[n];
}
// void X64Emitter::update_ctr_value(jit_value_t value) {
// XEASSERTNOTNULL(locals_.ctr);
void X64Emitter::update_cr_value(uint32_t n, GpVar& value) {
X86Compiler& c = compiler_;
XEASSERT(n >= 0 && n < 8);
XEASSERT(locals_.cr[n].getId() != kInvalidValue);
c.mov(locals_.cr[n], trunc(value, 1));
}
// // Extend to 64bits if needed.
// value = zero_extend(value, jit_type_nuint);
// jit_insn_store(fn_, locals_.ctr, value);
// }
#if 0
void X64Emitter::update_cr_with_cond(
uint32_t n, GpVar& lhs, GpVar& rhs, bool is_signed) {
X86Compiler& c = compiler_;
// bit0 = RA < RB
// bit1 = RA > RB
// bit2 = RA = RB
// bit3 = XER[SO]
// jit_value_t X64Emitter::cr_value(uint32_t n) {
// XEASSERT(n >= 0 && n < 8);
// XEASSERTNOTNULL(locals_.cr[n]);
// TODO(benvanik): inline this using the x86 cmp instruction - this prevents
// the need for a lot of the compares and ensures we lower to the best
// possible x86.
// GpVar& cmp = InlineAsm::get(
// FunctionType::get(),
// "cmp $0, $1 \n"
// "mov from compare registers \n",
// "r,r", ??
// true);
// jit_value_t value = jit_insn_load(fn_, locals_.cr[n]);
// value = zero_extend(value, jit_type_nuint);
// return value;
// }
// Convert input signs, if needed.
if (is_signed) {
lhs = make_signed(lhs);
rhs = make_signed(rhs);
} else {
lhs = make_unsigned(lhs);
rhs = make_unsigned(rhs);
}
GpVar& c = jit_insn_lt(fn_, lhs, rhs);
c = jit_insn_or(fn_, c,
jit_insn_shl(fn_, jit_insn_gt(fn_, lhs, rhs), get_uint32(1)));
c = jit_insn_or(fn_, c,
jit_insn_shl(fn_, jit_insn_eq(fn_, lhs, rhs), get_uint32(2)));
// void X64Emitter::update_cr_value(uint32_t n, jit_value_t value) {
// XEASSERT(n >= 0 && n < 8);
// XEASSERTNOTNULL(locals_.cr[n]);
// TODO(benvanik): set bit 4 to XER[SO]
// // Truncate to 8 bits if needed.
// // TODO(benvanik): also widen?
// value = trunc_to_ubyte(value);
// Insert the 4 bits into their location in the CR.
update_cr_value(n, c);
}
#endif
// jit_insn_store(fn_, locals_.cr[n], value);
// }
GpVar& X64Emitter::gpr_value(uint32_t n) {
X86Compiler& c = compiler_;
XEASSERT(n >= 0 && n < 32);
XEASSERT(locals_.gpr[n].getId() != kInvalidValue);
// void X64Emitter::update_cr_with_cond(
// uint32_t n, jit_value_t lhs, jit_value_t rhs, bool is_signed) {
// // bit0 = RA < RB
// // bit1 = RA > RB
// // bit2 = RA = RB
// // bit3 = XER[SO]
// Actually r0 is writable, even though nobody should ever do that.
// Perhaps we can check usage and enable this if safe?
// if (n == 0) {
// return get_uint64(0);
// }
// // TODO(benvanik): inline this using the x86 cmp instruction - this prevents
// // the need for a lot of the compares and ensures we lower to the best
// // possible x86.
// // jit_value_t cmp = InlineAsm::get(
// // FunctionType::get(),
// // "cmp $0, $1 \n"
// // "mov from compare registers \n",
// // "r,r", ??
// // true);
return locals_.gpr[n];
}
// // Convert input signs, if needed.
// if (is_signed) {
// lhs = make_signed(lhs);
// rhs = make_signed(rhs);
// } else {
// lhs = make_unsigned(lhs);
// rhs = make_unsigned(rhs);
// }
// jit_value_t c = jit_insn_lt(fn_, lhs, rhs);
// c = jit_insn_or(fn_, c,
// jit_insn_shl(fn_, jit_insn_gt(fn_, lhs, rhs), get_uint32(1)));
// c = jit_insn_or(fn_, c,
// jit_insn_shl(fn_, jit_insn_eq(fn_, lhs, rhs), get_uint32(2)));
void X64Emitter::update_gpr_value(uint32_t n, GpVar& value) {
X86Compiler& c = compiler_;
XEASSERT(n >= 0 && n < 32);
XEASSERT(locals_.gpr[n].getId() != kInvalidValue);
// // TODO(benvanik): set bit 4 to XER[SO]
// See above - r0 can be written.
// if (n == 0) {
// // Ignore writes to zero.
// return;
// }
// // Insert the 4 bits into their location in the CR.
// update_cr_value(n, c);
// }
c.mov(locals_.gpr[n], zero_extend(value, 8));
}
// jit_value_t X64Emitter::gpr_value(uint32_t n) {
// XEASSERT(n >= 0 && n < 32);
// XEASSERTNOTNULL(locals_.gpr[n]);
GpVar& X64Emitter::fpr_value(uint32_t n) {
X86Compiler& c = compiler_;
XEASSERT(n >= 0 && n < 32);
XEASSERT(locals_.fpr[n].getId() != kInvalidValue);
return locals_.fpr[n];
}
// // Actually r0 is writable, even though nobody should ever do that.
// // Perhaps we can check usage and enable this if safe?
// // if (n == 0) {
// // return get_uint64(0);
// // }
void X64Emitter::update_fpr_value(uint32_t n, GpVar& value) {
X86Compiler& c = compiler_;
XEASSERT(n >= 0 && n < 32);
XEASSERT(locals_.fpr[n].getId() != kInvalidValue);
c.mov(locals_.fpr[n], value);
}
// return jit_insn_load(fn_, locals_.gpr[n]);
// }
// void X64Emitter::update_gpr_value(uint32_t n, jit_value_t value) {
// XEASSERT(n >= 0 && n < 32);
// XEASSERTNOTNULL(locals_.gpr[n]);
// // See above - r0 can be written.
// // if (n == 0) {
// // // Ignore writes to zero.
// // return;
// // }
// // Extend to 64bits if needed.
// value = zero_extend(value, jit_type_nuint);
// jit_insn_store(fn_, locals_.gpr[n], value);
// }
// jit_value_t X64Emitter::fpr_value(uint32_t n) {
// XEASSERT(n >= 0 && n < 32);
// XEASSERTNOTNULL(locals_.fpr[n]);
// return jit_insn_load(fn_, locals_.fpr[n]);
// }
// void X64Emitter::update_fpr_value(uint32_t n, jit_value_t value) {
// XEASSERT(n >= 0 && n < 32);
// XEASSERTNOTNULL(locals_.fpr[n]);
// jit_insn_store(fn_, locals_.fpr[n], value);
// }
// jit_value_t X64Emitter::TouchMemoryAddress(uint32_t cia, jit_value_t addr) {
// GpVar& X64Emitter::TouchMemoryAddress(uint32_t cia, GpVar& addr) {
// // Input address is always in 32-bit space.
// addr = jit_insn_and(fn_,
// zero_extend(addr, jit_type_nuint),
@@ -1471,8 +1328,8 @@ void X64Emitter::SpillRegisters() {
// return addr;
// }
// jit_value_t X64Emitter::ReadMemory(
// uint32_t cia, jit_value_t addr, uint32_t size, bool acquire) {
// GpVar& X64Emitter::ReadMemory(
// uint32_t cia, GpVar& addr, uint32_t size, bool acquire) {
// jit_type_t data_type = NULL;
// bool needs_swap = false;
// switch (size) {
@@ -1517,7 +1374,7 @@ void X64Emitter::SpillRegisters() {
// }
// void X64Emitter::WriteMemory(
// uint32_t cia, jit_value_t addr, uint32_t size, jit_value_t value,
// uint32_t cia, GpVar& addr, uint32_t size, GpVar& value,
// bool release) {
// jit_type_t data_type = NULL;
// bool needs_swap = false;
@@ -1564,3 +1421,111 @@ void X64Emitter::SpillRegisters() {
// jit_value_t address = TouchMemoryAddress(cia, addr);
// jit_insn_store_relative(fn_, address, 0, value);
// }
// jit_value_t X64Emitter::get_int32(int32_t value) {
// return jit_value_create_nint_constant(fn_, jit_type_int, value);
// }
// jit_value_t X64Emitter::get_uint32(uint32_t value) {
// return jit_value_create_nint_constant(fn_, jit_type_uint, value);
// }
// jit_value_t X64Emitter::get_int64(int64_t value) {
// return jit_value_create_nint_constant(fn_, jit_type_nint, value);
// }
// jit_value_t X64Emitter::get_uint64(uint64_t value) {
// return jit_value_create_nint_constant(fn_, jit_type_nuint, value);
// }
#if 0
GpVar X64Emitter::sign_extend(GpVar& value, int size) {
X86Compiler& c = compiler_;
// No-op if the same size.
if (value.getSize() == size) {
return value;
}
// TODO(benvanik): use movsx if value is in memory.
// errrr.... could pin values to rax for cbw/cwde/cdqe
// or, could use shift trick (may be slower):
// shlq $(target_len-src_len), reg
// sarq $(target_len-src_len), reg
GpVar tmp;
switch (size) {
case 1:
tmp = c.newGpVar(kX86VarTypeGpd);
return value.r8();
case 2:
tmp = c.newGpVar(kX86VarTypeGpd);
return value.r16();
case 4:
tmp = c.newGpVar(kX86VarTypeGpd);
return value.r32();
default:
case 8:
tmp = c.newGpVar(kX86VarTypeGpq);
c.mov(tmp, value);
if (value.getSize() == 4) {
c.cdqe(value);
}
return value.r64();
}
}
#endif
GpVar X64Emitter::zero_extend(GpVar& value, int size) {
X86Compiler& c = compiler_;
// No-op if the same size.
if (value.getSize() == size) {
return value;
}
// TODO(benvanik): use movzx if value is in memory.
GpVar tmp;
switch (size) {
case 1:
tmp = c.newGpVar(kX86VarTypeGpd);
c.mov(tmp, value.r8());
break;
case 2:
tmp = c.newGpVar(kX86VarTypeGpd);
c.mov(tmp, value.r16());
break;
case 4:
tmp = c.newGpVar(kX86VarTypeGpd);
c.mov(tmp, value.r32());
break;
default:
case 8:
tmp = c.newGpVar(kX86VarTypeGpq);
c.mov(tmp, value.r64());
break;
}
return tmp;
}
GpVar X64Emitter::trunc(GpVar& value, int size) {
X86Compiler& c = compiler_;
// No-op if the same size.
if (value.getSize() == size) {
return value;
}
switch (size) {
case 1:
return value.r8();
case 2:
return value.r16();
case 4:
return value.r32();
default:
case 8:
return value.r64();
}
}