diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc index e6f1d83cb5..01b366431b 100644 --- a/src/ia32/assembler-ia32.cc +++ b/src/ia32/assembler-ia32.cc @@ -730,6 +730,33 @@ void Assembler::xchg_w(Register reg, const Operand& op) { emit_operand(reg, op); } +void Assembler::lock() { + EnsureSpace ensure_space(this); + EMIT(0xF0); +} + +void Assembler::cmpxchg(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xB1); + emit_operand(src, dst); +} + +void Assembler::cmpxchg_b(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0xB0); + emit_operand(src, dst); +} + +void Assembler::cmpxchg_w(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + EMIT(0x66); + EMIT(0x0F); + EMIT(0xB1); + emit_operand(src, dst); +} + void Assembler::adc(Register dst, int32_t imm32) { EnsureSpace ensure_space(this); emit_arith(2, Operand(dst), Immediate(imm32)); diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h index c3edacb0ac..f2e7c6cfd4 100644 --- a/src/ia32/assembler-ia32.h +++ b/src/ia32/assembler-ia32.h @@ -662,6 +662,14 @@ class Assembler : public AssemblerBase { void xchg_b(Register reg, const Operand& op); void xchg_w(Register reg, const Operand& op); + // Lock prefix + void lock(); + + // CompareExchange + void cmpxchg(const Operand& dst, Register src); + void cmpxchg_b(const Operand& dst, Register src); + void cmpxchg_w(const Operand& dst, Register src); + // Arithmetics void adc(Register dst, int32_t imm32); void adc(Register dst, const Operand& src); diff --git a/src/ia32/disasm-ia32.cc b/src/ia32/disasm-ia32.cc index a87e44b3f9..d776365b59 100644 --- a/src/ia32/disasm-ia32.cc +++ b/src/ia32/disasm-ia32.cc @@ -1232,6 +1232,10 @@ static const char* F0Mnem(byte f0byte) { return "shrd"; // 3-operand version. case 0xAB: return "bts"; + case 0xB0: + return "cmpxchg_b"; + case 0xB1: + return "cmpxchg"; case 0xBC: return "bsf"; case 0xBD: @@ -1264,6 +1268,9 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, vex_byte0_ = *data; vex_byte1_ = *(data + 1); data += 2; + } else if (*data == 0xF0 /*lock*/) { + AppendToBuffer("lock "); + data++; } bool processed = true; // Will be set to false if the current instruction @@ -1496,6 +1503,18 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, } else { AppendToBuffer(",%s,cl", NameOfCPURegister(regop)); } + } else if (f0byte == 0xB0) { + // cmpxchg_b + data += 2; + AppendToBuffer("%s ", f0mnem); + int mod, regop, rm; + get_modrm(*data, &mod, ®op, &rm); + data += PrintRightOperand(data); + AppendToBuffer(",%s", NameOfByteCPURegister(regop)); + } else if (f0byte == 0xB1) { + // cmpxchg + data += 2; + data += PrintOperands(f0mnem, OPER_REG_OP_ORDER, data); } else if (f0byte == 0xBC) { data += 2; int mod, regop, rm; @@ -1612,9 +1631,8 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, data++; int mod, regop, rm; get_modrm(*data, &mod, ®op, &rm); - AppendToBuffer("xchg_w "); + AppendToBuffer("xchg_w %s,", NameOfCPURegister(regop)); data += PrintRightOperand(data); - AppendToBuffer(",%s", NameOfCPURegister(regop)); } else if (*data == 0x89) { data++; int mod, regop, rm; @@ -1884,6 +1902,9 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, NameOfXMMRegister(regop), NameOfXMMRegister(rm)); data++; + } else if (*data == 0xB1) { + data++; + data += PrintOperands("cmpxchg_w", OPER_REG_OP_ORDER, data); } else { UnimplementedInstruction(); } diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc index 5f8fb6818e..4b9115d43b 100644 --- a/src/x64/assembler-x64.cc +++ b/src/x64/assembler-x64.cc @@ -1016,6 +1016,40 @@ void Assembler::cmpb_al(Immediate imm8) { emit(imm8.value_); } +void Assembler::lock() { + EnsureSpace ensure_space(this); + emit(0xf0); +} + +void Assembler::cmpxchgb(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + if (!src.is_byte_register()) { + // Register is not one of al, bl, cl, dl. Its encoding needs REX. + emit_rex_32(src, dst); + } else { + emit_optional_rex_32(src, dst); + } + emit(0x0f); + emit(0xb0); + emit_operand(src, dst); +} + +void Assembler::cmpxchgw(const Operand& dst, Register src) { + EnsureSpace ensure_space(this); + emit(0x66); + emit_optional_rex_32(src, dst); + emit(0x0f); + emit(0xb1); + emit_operand(src, dst); +} + +void Assembler::emit_cmpxchg(const Operand& dst, Register src, int size) { + EnsureSpace ensure_space(this); + emit_rex(src, dst, size); + emit(0x0f); + emit(0xb1); + emit_operand(src, dst); +} void Assembler::cpuid() { EnsureSpace ensure_space(this); diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h index 77a1a57889..4df1801801 100644 --- a/src/x64/assembler-x64.h +++ b/src/x64/assembler-x64.h @@ -421,11 +421,11 @@ class Operand BASE_EMBEDDED { friend class Assembler; }; - #define ASSEMBLER_INSTRUCTION_LIST(V) \ V(add) \ V(and) \ V(cmp) \ + V(cmpxchg) \ V(dec) \ V(idiv) \ V(div) \ @@ -445,7 +445,6 @@ class Operand BASE_EMBEDDED { V(xchg) \ V(xor) - // Shift instructions on operands/registers with kPointerSize, kInt32Size and // kInt64Size. #define SHIFT_INSTRUCTION_LIST(V) \ @@ -788,9 +787,15 @@ class Assembler : public AssemblerBase { void decb(Register dst); void decb(const Operand& dst); + // Lock prefix. + void lock(); + void xchgb(Register reg, const Operand& op); void xchgw(Register reg, const Operand& op); + void cmpxchgb(const Operand& dst, Register src); + void cmpxchgw(const Operand& dst, Register src); + // Sign-extends rax into rdx:rax. void cqo(); // Sign-extends eax into edx:eax. @@ -2054,6 +2059,11 @@ class Assembler : public AssemblerBase { immediate_arithmetic_op(0x7, dst, src, size); } + // Compare {al,ax,eax,rax} with src. If equal, set ZF and write dst into + // src. Otherwise clear ZF and write src into {al,ax,eax,rax}. This + // operation is only atomic if prefixed by the lock instruction. + void emit_cmpxchg(const Operand& dst, Register src, int size); + void emit_dec(Register dst, int size); void emit_dec(const Operand& dst, int size); diff --git a/src/x64/disasm-x64.cc b/src/x64/disasm-x64.cc index 7126b89b99..0f5230a19f 100644 --- a/src/x64/disasm-x64.cc +++ b/src/x64/disasm-x64.cc @@ -142,19 +142,18 @@ enum InstructionType { SHORT_IMMEDIATE_INSTR }; - enum Prefixes { ESCAPE_PREFIX = 0x0F, OPERAND_SIZE_OVERRIDE_PREFIX = 0x66, ADDRESS_SIZE_OVERRIDE_PREFIX = 0x67, VEX3_PREFIX = 0xC4, VEX2_PREFIX = 0xC5, + LOCK_PREFIX = 0xF0, REPNE_PREFIX = 0xF2, REP_PREFIX = 0xF3, REPEQ_PREFIX = REP_PREFIX }; - struct InstructionDesc { const char* mnem; InstructionType type; @@ -1607,6 +1606,8 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { AppendToBuffer("%s %s,%d", (regop == 6) ? "psllq" : "psrlq", NameOfXMMRegister(rm), *current & 0x7f); current += 1; + } else if (opcode == 0xB1) { + current += PrintOperands("cmpxchg", OPER_REG_OP_ORDER, current); } else { const char* mnemonic = "?"; if (opcode == 0x54) { @@ -1884,6 +1885,12 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) { current += PrintRightOperand(current); } else if (opcode == 0x0B) { AppendToBuffer("ud2"); + } else if (opcode == 0xB0 || opcode == 0xB1) { + // CMPXCHG. + if (opcode == 0xB0) { + byte_size_operand_ = true; + } + current += PrintOperands(mnemonic, OPER_REG_OP_ORDER, current); } else { UnimplementedInstruction(); } @@ -1926,6 +1933,9 @@ const char* DisassemblerX64::TwoByteMnemonic(byte opcode) { return "shrd"; case 0xAF: return "imul"; + case 0xB0: + case 0xB1: + return "cmpxchg"; case 0xB6: return "movzxb"; case 0xB7: @@ -1963,6 +1973,8 @@ int DisassemblerX64::InstructionDecode(v8::internal::Vector out_buffer, if (rex_w()) AppendToBuffer("REX.W "); } else if ((current & 0xFE) == 0xF2) { // Group 1 prefix (0xF2 or 0xF3). group_1_prefix_ = current; + } else if (current == LOCK_PREFIX) { + AppendToBuffer("lock "); } else if (current == VEX3_PREFIX) { vex_byte0_ = current; vex_byte1_ = *(data + 1); diff --git a/test/cctest/test-disasm-ia32.cc b/test/cctest/test-disasm-ia32.cc index 3ecd7b3256..e0fe9697ba 100644 --- a/test/cctest/test-disasm-ia32.cc +++ b/test/cctest/test-disasm-ia32.cc @@ -665,12 +665,30 @@ TEST(DisasmIa320) { // xchg. { + __ xchg_b(eax, Operand(eax, 8)); + __ xchg_w(eax, Operand(ebx, 8)); __ xchg(eax, eax); __ xchg(eax, ebx); __ xchg(ebx, ebx); __ xchg(ebx, Operand(esp, 12)); } + // cmpxchg. + { + __ cmpxchg_b(Operand(esp, 12), eax); + __ cmpxchg_w(Operand(ebx, ecx, times_4, 10000), eax); + __ cmpxchg(Operand(ebx, ecx, times_4, 10000), eax); + } + + // lock prefix. + { + __ lock(); + __ cmpxchg(Operand(esp, 12), ebx); + + __ lock(); + __ xchg_w(eax, Operand(ecx, 8)); + } + // Nop instructions for (int i = 0; i < 16; i++) { __ Nop(i); diff --git a/test/cctest/test-disasm-x64.cc b/test/cctest/test-disasm-x64.cc index b855626cee..59ad683ba4 100644 --- a/test/cctest/test-disasm-x64.cc +++ b/test/cctest/test-disasm-x64.cc @@ -745,12 +745,31 @@ TEST(DisasmX64) { // xchg. { + __ xchgb(rax, Operand(rax, 8)); + __ xchgw(rax, Operand(rbx, 8)); __ xchgq(rax, rax); __ xchgq(rax, rbx); __ xchgq(rbx, rbx); __ xchgq(rbx, Operand(rsp, 12)); } + // cmpxchg. + { + __ cmpxchgb(Operand(rsp, 12), rax); + __ cmpxchgw(Operand(rbx, rcx, times_4, 10000), rax); + __ cmpxchgl(Operand(rbx, rcx, times_4, 10000), rax); + __ cmpxchgq(Operand(rbx, rcx, times_4, 10000), rax); + } + + // lock prefix. + { + __ lock(); + __ cmpxchgl(Operand(rsp, 12), rbx); + + __ lock(); + __ xchgw(rax, Operand(rcx, 8)); + } + // Nop instructions for (int i = 0; i < 16; i++) { __ Nop(i);