diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc index d7ce5a793a..7e59ab9fa8 100644 --- a/src/ia32/assembler-ia32.cc +++ b/src/ia32/assembler-ia32.cc @@ -1340,6 +1340,13 @@ void Assembler::ret(int imm16) { } +void Assembler::ud2() { + EnsureSpace ensure_space(this); + EMIT(0x0F); + EMIT(0x0B); +} + + // Labels refer to positions in the (to be) generated code. // There are bound, linked, and unused labels. // @@ -1378,7 +1385,10 @@ void Assembler::bind_to(Label* L, int pos) { while (L->is_linked()) { Displacement disp = disp_at(L); int fixup_pos = L->pos(); - if (disp.type() == Displacement::CODE_RELATIVE) { + if (disp.type() == Displacement::CODE_ABSOLUTE) { + long_at_put(fixup_pos, reinterpret_cast(buffer_ + pos)); + internal_reference_positions_.push_back(fixup_pos); + } else if (disp.type() == Displacement::CODE_RELATIVE) { // Relative to Code* heap object pointer. long_at_put(fixup_pos, pos + Code::kHeaderSize - kHeapObjectTag); } else { @@ -2690,15 +2700,10 @@ void Assembler::GrowBuffer() { reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta, reloc_info_writer.last_pc() + pc_delta); - // Relocate runtime entries. - for (RelocIterator it(desc); !it.done(); it.next()) { - RelocInfo::Mode rmode = it.rinfo()->rmode(); - if (rmode == RelocInfo::INTERNAL_REFERENCE) { - int32_t* p = reinterpret_cast(it.rinfo()->pc()); - if (*p != 0) { // 0 means uninitialized. - *p += pc_delta; - } - } + // Relocate internal references. + for (auto pos : internal_reference_positions_) { + int32_t* p = reinterpret_cast(buffer_ + pos); + *p += pc_delta; } DCHECK(!buffer_overflow()); @@ -2748,7 +2753,21 @@ void Assembler::emit_operand(Register reg, const Operand& adr) { if (length >= sizeof(int32_t) && !RelocInfo::IsNone(adr.rmode_)) { pc_ -= sizeof(int32_t); // pc_ must be *at* disp32 RecordRelocInfo(adr.rmode_); - pc_ += sizeof(int32_t); + if (adr.rmode_ == RelocInfo::INTERNAL_REFERENCE) { // Fixup for labels + emit_label(*reinterpret_cast(pc_)); + } else { + pc_ += sizeof(int32_t); + } + } +} + + +void Assembler::emit_label(Label* label) { + if (label->is_bound()) { + internal_reference_positions_.push_back(pc_offset()); + emit(reinterpret_cast(buffer_ + label->pos())); + } else { + emit_disp(label, Displacement::CODE_ABSOLUTE); } } @@ -2773,6 +2792,13 @@ void Assembler::dd(uint32_t data) { } +void Assembler::dd(Label* label) { + EnsureSpace ensure_space(this); + RecordRelocInfo(RelocInfo::INTERNAL_REFERENCE); + emit_label(label); +} + + void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { DCHECK(!RelocInfo::IsNone(rmode)); // Don't record external references unless the heap will be serialized. diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h index a5d289d970..71d894d870 100644 --- a/src/ia32/assembler-ia32.h +++ b/src/ia32/assembler-ia32.h @@ -37,6 +37,8 @@ #ifndef V8_IA32_ASSEMBLER_IA32_H_ #define V8_IA32_ASSEMBLER_IA32_H_ +#include + #include "src/isolate.h" #include "src/serialize.h" @@ -357,6 +359,11 @@ class Operand BASE_EMBEDDED { int32_t disp, RelocInfo::Mode rmode = RelocInfo::NONE32); + static Operand JumpTable(Register index, ScaleFactor scale, Label* table) { + return Operand(index, scale, reinterpret_cast(table), + RelocInfo::INTERNAL_REFERENCE); + } + static Operand StaticVariable(const ExternalReference& ext) { return Operand(reinterpret_cast(ext.address()), RelocInfo::EXTERNAL_REFERENCE); @@ -430,11 +437,7 @@ class Operand BASE_EMBEDDED { class Displacement BASE_EMBEDDED { public: - enum Type { - UNCONDITIONAL_JUMP, - CODE_RELATIVE, - OTHER - }; + enum Type { UNCONDITIONAL_JUMP, CODE_RELATIVE, OTHER, CODE_ABSOLUTE }; int data() const { return data_; } Type type() const { return TypeField::decode(data_); } @@ -804,6 +807,7 @@ class Assembler : public AssemblerBase { void int3(); void nop(); void ret(int imm16); + void ud2(); // Label operations & relative jumps (PPUM Appendix D) // @@ -1268,6 +1272,7 @@ class Assembler : public AssemblerBase { // inline tables, e.g., jump-tables. void db(uint8_t data); void dd(uint32_t data); + void dd(Label* label); // Check if there is less than kGap bytes available in the buffer. // If this is the case, we need to grow the buffer before emitting @@ -1343,6 +1348,8 @@ class Assembler : public AssemblerBase { void emit_operand(Register reg, const Operand& adr); + void emit_label(Label* label); + void emit_farith(int b1, int b2, int i); // Emit vex prefix @@ -1369,6 +1376,11 @@ class Assembler : public AssemblerBase { friend class CodePatcher; friend class EnsureSpace; + // Internal reference positions, required for (potential) patching in + // GrowBuffer(); contains only those internal references whose labels + // are already bound. + std::deque internal_reference_positions_; + // code generation RelocInfoWriter reloc_info_writer; diff --git a/src/ia32/disasm-ia32.cc b/src/ia32/disasm-ia32.cc index bf88f69c96..576c7393cc 100644 --- a/src/ia32/disasm-ia32.cc +++ b/src/ia32/disasm-ia32.cc @@ -1035,6 +1035,8 @@ int DisassemblerIA32::RegisterFPUInstruction(int escape_opcode, // Returns NULL if the instruction is not handled here. static const char* F0Mnem(byte f0byte) { switch (f0byte) { + case 0x0B: + return "ud2"; case 0x18: return "prefetch"; case 0xA2: return "cpuid"; case 0xBE: return "movsx_b"; @@ -1215,7 +1217,7 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector out_buffer, data[7] == 0) { AppendToBuffer("nop"); // 8 byte nop. data += 8; - } else if (f0byte == 0xA2 || f0byte == 0x31) { + } else if (f0byte == 0x0B || f0byte == 0xA2 || f0byte == 0x31) { AppendToBuffer("%s", f0mnem); data += 2; } else if (f0byte == 0x28) { diff --git a/test/cctest/test-assembler-ia32.cc b/test/cctest/test-assembler-ia32.cc index b68cbec037..46592a05d1 100644 --- a/test/cctest/test-assembler-ia32.cc +++ b/test/cctest/test-assembler-ia32.cc @@ -1043,4 +1043,100 @@ TEST(AssemblerX64FMA_ss) { F10 f = FUNCTION_CAST(code->entry()); CHECK_EQ(0, f(9.26621069e-05f, -2.4607749f, -1.09587872f)); } + + +TEST(AssemblerIa32JumpTables1) { + // Test jump tables with forward jumps. + CcTest::InitializeVM(); + Isolate* isolate = reinterpret_cast(CcTest::isolate()); + HandleScope scope(isolate); + Assembler assm(isolate, nullptr, 0); + + const int kNumCases = 512; + int values[kNumCases]; + isolate->random_number_generator()->NextBytes(values, sizeof(values)); + Label labels[kNumCases]; + + Label done, table; + __ mov(eax, Operand(esp, 4)); + __ jmp(Operand::JumpTable(eax, times_4, &table)); + __ ud2(); + __ bind(&table); + for (int i = 0; i < kNumCases; ++i) { + __ dd(&labels[i]); + } + + for (int i = 0; i < kNumCases; ++i) { + __ bind(&labels[i]); + __ mov(eax, Immediate(values[i])); + __ jmp(&done); + } + + __ bind(&done); + __ ret(0); + + CodeDesc desc; + assm.GetCode(&desc); + Handle code = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle()); +#ifdef OBJECT_PRINT + OFStream os(stdout); + code->Print(os); +#endif + F1 f = FUNCTION_CAST(code->entry()); + for (int i = 0; i < kNumCases; ++i) { + int res = f(i); + ::printf("f(%d) = %d\n", i, res); + CHECK_EQ(values[i], res); + } +} + + +TEST(AssemblerIa32JumpTables2) { + // Test jump tables with backward jumps. + CcTest::InitializeVM(); + Isolate* isolate = reinterpret_cast(CcTest::isolate()); + HandleScope scope(isolate); + Assembler assm(isolate, nullptr, 0); + + const int kNumCases = 512; + int values[kNumCases]; + isolate->random_number_generator()->NextBytes(values, sizeof(values)); + Label labels[kNumCases]; + + Label done, table; + __ mov(eax, Operand(esp, 4)); + __ jmp(Operand::JumpTable(eax, times_4, &table)); + __ ud2(); + + for (int i = 0; i < kNumCases; ++i) { + __ bind(&labels[i]); + __ mov(eax, Immediate(values[i])); + __ jmp(&done); + } + + __ bind(&table); + for (int i = 0; i < kNumCases; ++i) { + __ dd(&labels[i]); + } + + __ bind(&done); + __ ret(0); + + CodeDesc desc; + assm.GetCode(&desc); + Handle code = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle()); +#ifdef OBJECT_PRINT + OFStream os(stdout); + code->Print(os); +#endif + F1 f = FUNCTION_CAST(code->entry()); + for (int i = 0; i < kNumCases; ++i) { + int res = f(i); + ::printf("f(%d) = %d\n", i, res); + CHECK_EQ(values[i], res); + } +} + #undef __