Skip to content

Commit

Permalink
Added li_ptr to create 48 bit immediate addresses.
Browse files Browse the repository at this point in the history
  • Loading branch information
DrRezaYazdani authored and obycode committed Oct 15, 2020
1 parent b3c1fb8 commit 5f073e8
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 85 deletions.
128 changes: 63 additions & 65 deletions src/codegen/riscv64/assembler-riscv64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,9 @@ bool Assembler::IsAddiw(Instr instr) {
bool Assembler::IsAddi(Instr instr) {
return (instr & (kBaseOpcodeMask | kFunct3Mask)) == RO_ADDI;
}
bool Assembler::IsOri(Instr instr) {
return (instr & (kBaseOpcodeMask | kFunct3Mask)) == RO_ORI;
}
bool Assembler::IsSlli(Instr instr) {
return (instr & (kBaseOpcodeMask | kFunct3Mask)) == RO_SLLI;
}
Expand Down Expand Up @@ -2377,6 +2380,24 @@ int Assembler::li_estimate(int64_t imm, bool is_get_temp_reg) {
return count;
}

void Assembler::li_ptr(Register rd, int64_t imm) {
// Initialize rd with an address
// Pointers are 48 bits
// 6 fixed instructions are generated
DCHECK((imm & 0xfff0000000000000ll) == 0);
int64_t a6 = imm & 0x3f; // bits 0:6. 6 bits
int64_t b11 = (imm >> 6) & 0x7ff; //bits 6:11. 11 bits
int64_t high_31 = (imm >> 17) & 0x7fffffff; // 31 bits
int64_t high_20 = ((high_31 + 0x800) >> 12); // 19 bits
int64_t low_12 = high_31 & 0xfff; // 12 bits
lui(rd, (int32_t)high_20);
addi(rd, rd, low_12); // 31 bits in rd.
slli(rd, rd, 11); // Space for next 11 bis
ori(rd, rd, b11); // 11 bits are put in. 42 bit in rd
slli(rd, rd, 6); // Space for next 6 bits
ori(rd, rd, a6); // 6 bits are put in. 48 bis in rd
}

void Assembler::li_constant(Register rd, int64_t imm) {
DEBUG_PRINTF("li_constant(%d, %lx <%ld>)\n", ToNumber(rd), imm, imm);
lui(rd, (imm + (1LL << 47) + (1LL << 35) + (1LL << 23) + (1LL << 11)) >>
Expand Down Expand Up @@ -2661,52 +2682,46 @@ Address Assembler::target_address_at(Address pc, Address constant_pool) {
return target_address_at(pc);
}
}

Address Assembler::target_address_at(Address pc) {
DEBUG_PRINTF("target_address_at: pc: %lx\t", pc);
Instruction* instr0 = Instruction::At((unsigned char*)pc);
Instruction* instr1 = Instruction::At((unsigned char*)(pc + 1 * kInstrSize));
Instruction* instr2 = Instruction::At((unsigned char*)(pc + 2 * kInstrSize));
Instruction* instr3 = Instruction::At((unsigned char*)(pc + 3 * kInstrSize));
Instruction* instr4 = Instruction::At((unsigned char*)(pc + 4 * kInstrSize));
Instruction* instr5 = Instruction::At((unsigned char*)(pc + 5 * kInstrSize));
Instruction* instr7 = Instruction::At((unsigned char*)(pc + 7 * kInstrSize));

// Interpret 5 instructions for address generated by li: See listing in
// Interpret instructions for address generated by li: See listing in
// Assembler::set_target_address_at() just below.
if (IsLui(*reinterpret_cast<Instr*>(instr0)) &&
IsAddiw(*reinterpret_cast<Instr*>(instr1)) &&
IsAddi(*reinterpret_cast<Instr*>(instr3)) &&
IsAddi(*reinterpret_cast<Instr*>(instr5)) &&
IsAddi(*reinterpret_cast<Instr*>(instr7))) {
IsAddi(*reinterpret_cast<Instr*>(instr1)) &&
IsSlli(*reinterpret_cast<Instr*>(instr2)) &&
IsOri(*reinterpret_cast<Instr*>(instr3)) &&
IsSlli(*reinterpret_cast<Instr*>(instr4)) &&
IsOri(*reinterpret_cast<Instr*>(instr5))) {
// Assemble the 64 bit value.
int64_t addr = (int64_t)(instr0->Imm20UValue() << kImm20Shift) +
(int64_t)instr1->Imm12Value();
addr <<= 12;
addr += (int64_t)instr3->Imm12Value();
addr <<= 12;
addr += (int64_t)instr5->Imm12Value();
addr <<= 12;
addr += (int64_t)instr7->Imm12Value();
addr <<= 11;
addr |= (int64_t)instr3->Imm12Value();
addr <<= 6;
addr |= (int64_t)instr5->Imm12Value();

DEBUG_PRINTF("addr: %lx\n", addr);
return static_cast<Address>(addr);
}
// We should never get here, force a bad address if we do.
UNREACHABLE();
}

// On RISC-V, a 64-bit target address is stored in an 8-instruction sequence:
// 0: lui(rd, (j.imm64_ + (1LL << 47) + (1LL << 35) +
// (1LL << 23) + (1LL << 11)) >> 48);
// 1: addiw(rd, rd, (j.imm64_ + (1LL << 35) + (1LL << 23) + (1LL << 11))
// << 16 >> 52);
// 2: slli(rd, rd, 12);
// 3: addi(rd, rd, (j.imm64_ + (1LL << 23) + (1LL << 11)) << 28 >> 52);
// 4: slli(rd, rd, 12);
// 5: addi(rd, rd, (j.imm64_ + (1 << 11)) << 40 >> 52);
// 6: slli(rd, rd, 12);
// 7: addi(rd, rd, j.imm64_ << 52 >> 52);
// On RISC-V, a 48-bit target address is stored in an 6-instruction sequence:
// lui(reg, (int32_t)high_20); // 19 high bits
// addi(reg, reg, low_12); // 12 following bits. total is 31 high bits in reg.
// slli(reg, reg, 11); // Space for next 11 bits
// ori(reg, reg, b11); // 11 bits are put in. 42 bit in reg
// slli(reg, reg, 6); // Space for next 6 bits
// ori(reg, reg, a6); // 6 bits are put in. all 48 bis in reg
//
// Patching the address must replace all the lui & addi instructions,
// Patching the address must replace all instructions,
// and flush the i-cache.
void Assembler::set_target_value_at(Address pc, uint64_t target,
ICacheFlushMode icache_flush_mode) {
Expand All @@ -2717,59 +2732,42 @@ void Assembler::set_target_value_at(Address pc, uint64_t target,
// It relies on fact the upper [63:48] bits are not used for virtual address
// translation and they have to be set according to value of bit 47 in order
// get canonical address.
Instruction* instr0 = Instruction::At((unsigned char*)pc);
DEBUG_PRINTF("set_target_value_at: pc: %lx\ttarget: %lx\n", pc, target);
int rd_code = instr0->RdValue();
uint32_t* p = reinterpret_cast<uint32_t*>(pc);

DCHECK((target & 0xffff000000000000ll) == 0);
#ifdef DEBUG
// Check we have the result from a li macro-instruction.
Instruction* instr0 = Instruction::At((unsigned char*)pc);
Instruction* instr1 = Instruction::At((unsigned char*)(pc + 1 * kInstrSize));
Instruction* instr3 = Instruction::At((unsigned char*)(pc + 3 * kInstrSize));
Instruction* instr5 = Instruction::At((unsigned char*)(pc + 5 * kInstrSize));
Instruction* instr7 = Instruction::At((unsigned char*)(pc + 7 * kInstrSize));
DCHECK(IsLui(*reinterpret_cast<Instr*>(instr0)) &&
IsAddiw(*reinterpret_cast<Instr*>(instr1)) &&
IsAddi(*reinterpret_cast<Instr*>(instr3)) &&
IsAddi(*reinterpret_cast<Instr*>(instr5)) &&
IsAddi(*reinterpret_cast<Instr*>(instr7)));
IsAddi(*reinterpret_cast<Instr*>(instr1)) &&
IsOri(*reinterpret_cast<Instr*>(instr3)) &&
IsOri(*reinterpret_cast<Instr*>(instr5)));
#endif

// Must use 8 instructions to insure patchable code (see above comment).
*p = LUI | (rd_code << kRdShift) |
((uint32_t)(
(target + (1LL << 47) + (1LL << 35) + (1LL << 23) + (1LL << 11)) >>
48)
<< kImm20Shift);
*(p + 1) =
OP_IMM_32 | (rd_code << kRdShift) | (0b000 << kFunct3Shift) |
(rd_code << kRs1Shift) |
((uint32_t)((target + (1LL << 35) + (1LL << 23) + (1LL << 11)) << 16 >>
52)
<< kImm12Shift);
*(p + 2) = OP_IMM | (rd_code << kRdShift) | (0b001 << kFunct3Shift) |
(rd_code << kRs1Shift) | (12 << kImm12Shift);
*(p + 3) = OP_IMM | (rd_code << kRdShift) | (0b000 << kFunct3Shift) |
(rd_code << kRs1Shift) |
((uint32_t)((target + (1LL << 23) + (1LL << 11)) << 28 >> 52)
<< kImm12Shift);
*(p + 4) = OP_IMM | (rd_code << kRdShift) | (0b001 << kFunct3Shift) |
(rd_code << kRs1Shift) | (12 << kImm12Shift);
*(p + 5) = OP_IMM | (rd_code << kRdShift) | (0b000 << kFunct3Shift) |
(rd_code << kRs1Shift) |
((uint32_t)((target + (1LL << 11)) << 40 >> 52) << kImm12Shift);
*(p + 6) = OP_IMM | (rd_code << kRdShift) | (0b001 << kFunct3Shift) |
(rd_code << kRs1Shift) | (12 << kImm12Shift);
*(p + 7) = OP_IMM | (rd_code << kRdShift) | (0b000 << kFunct3Shift) |
(rd_code << kRs1Shift) |
((uint32_t)(target << 52 >> 52) << kImm12Shift);

int64_t a6 = target & 0x3f; // bits 0:6. 6 bits
int64_t b11 = (target >> 6) & 0x7ff; //bits 6:11. 11 bits
int64_t high_31 = (target >> 17) & 0x7fffffff; // 31 bits
int64_t high_20 = ((high_31 + 0x800) >> 12); // 19 bits
int64_t low_12 = high_31 & 0xfff; // 12 bits
*p = *p & 0xfff;
*p = *p | ((int32_t)high_20 << 12);
*(p + 1) = *(p + 1) & 0xfffff;
*(p + 1) = *(p + 1) | ((int32_t)low_12 << 20);
*(p + 2) = *(p + 2) & 0xfffff;
*(p + 2) = *(p + 2) | (11 << 20);
*(p + 3) = *(p + 3) & 0xfffff;
*(p + 3) = *(p + 3) | ((int32_t)b11 << 20);
*(p + 4) = *(p + 4) & 0xfffff;
*(p + 4) = *(p + 4) | (6 << 20);
*(p + 5) = *(p + 5) & 0xfffff;
*(p + 5) = *(p + 5) | ((int32_t)a6 << 20);
if (icache_flush_mode != SKIP_ICACHE_FLUSH) {
FlushInstructionCache(pc, 8 * kInstrSize);
}
DCHECK_EQ(target_address_at(pc), target);
}

UseScratchRegisterScope::UseScratchRegisterScope(Assembler* assembler)
: available_(assembler->GetScratchRegisterList()),
old_available_(*available_) {}
Expand Down
2 changes: 2 additions & 0 deletions src/codegen/riscv64/assembler-riscv64.h
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
// Loads an immediate, always using 8 instructions, regardless of the value,
// so that it can be modified later.
void li_constant(Register rd, int64_t imm);
void li_ptr(Register rd, int64_t imm);

void mv(Register rd, Register rs) { addi(rd, rs, 0); }
void not_(Register rd, Register rs) { xori(rd, rs, -1); }
Expand Down Expand Up @@ -837,6 +838,7 @@ class V8_EXPORT_PRIVATE Assembler : public AssemblerBase {
static bool IsAuipc(Instr instr);
static bool IsAddiw(Instr instr);
static bool IsAddi(Instr instr);
static bool IsOri(Instr instr);
static bool IsSlli(Instr instr);
static bool IsLd(Instr instr);

Expand Down
11 changes: 5 additions & 6 deletions src/codegen/riscv64/macro-assembler-riscv64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1462,16 +1462,15 @@ void TurboAssembler::li(Register rd, Operand j, LiFlags mode) {
}

RecordRelocInfo(j.rmode(), immediate);
// FIXME(RISC_V): Does this case need to be constant size?
li_constant(rd, immediate);
li_ptr(rd, immediate);
} else if (mode == ADDRESS_LOAD) {
// We always need the same number of instructions as we may need to patch
// this code to load another value which may need all 8 instructions.
// this code to load another value which may need all 6 instructions.
RecordRelocInfo(j.rmode());
li_constant(rd, j.immediate());
} else { // mode == CONSTANT_SIZE - always emit the same instruction
li_ptr(rd, j.immediate());
} else { // Always emit the same 48 bit instruction
// sequence.
li_constant(rd, j.immediate());
li_ptr(rd, j.immediate());
}
}

Expand Down
22 changes: 10 additions & 12 deletions test/cctest/test-assembler-riscv64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1334,6 +1334,7 @@ TEST(RVC_CA) {
auto res = GenAndRunTest<int64_t>(LARGE_INT_UNDER_32_BIT, fn);
CHECK_EQ(LARGE_INT_UNDER_32_BIT - MIN_VAL_IMM12, res);
}

// Test c.addw
{
auto fn = [](MacroAssembler& assm) {
Expand Down Expand Up @@ -1464,35 +1465,32 @@ TEST(TARGET_ADDR) {
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);

// This is the series of instructions to load 0x123456789abcdef0
uint32_t buffer[8] = {0x01234237, 0x5682021b, 0x00c21213, 0x89b20213,
0x00c21213, 0xbce20213, 0x00c21213, 0xef020213};

// This is the series of instructions to load 48 bit address 0x0123456789ab
uint32_t buffer[6] = {0x091ab37, 0x2b330213, 0x00b21213, 0x62626213,
0x00621213, 0x02b26213};
MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes);

uintptr_t addr = reinterpret_cast<uintptr_t>(&buffer[0]);
Address res = __ target_address_at(static_cast<Address>(addr));

CHECK_EQ(0x123456789abcdef0L, res);
CHECK_EQ(0x0123456789abL, res);
}

TEST(SET_TARGET_ADDR) {
CcTest::InitializeVM();
Isolate* isolate = CcTest::i_isolate();
HandleScope scope(isolate);

// This is the series of instructions to load 0x123456789abcdef0
uint32_t buffer[8] = {0x01234237, 0x5682021b, 0x00c21213, 0x89b20213,
0x00c21213, 0xbce20213, 0x00c21213, 0xef020213};
// This is the series of instructions to load 48 bit address 0xba9876543210
uint32_t buffer[6] = {0x091ab37, 0x2b330213, 0x00b21213, 0x62626213,
0x00621213, 0x02b26213};

MacroAssembler assm(isolate, v8::internal::CodeObjectRequired::kYes);

uintptr_t addr = reinterpret_cast<uintptr_t>(&buffer[0]);
__ set_target_value_at(static_cast<Address>(addr), 0xfedcba9876543210,
__ set_target_value_at(static_cast<Address>(addr), 0xba9876543210L,
FLUSH_ICACHE_IF_NEEDED);
Address res = __ target_address_at(static_cast<Address>(addr));

CHECK_EQ(0xfedcba9876543210, res);
CHECK_EQ(0xba9876543210L, res);
}

// pair.first is the F_TYPE input to test, pair.second is I_TYPE expected
Expand Down
4 changes: 2 additions & 2 deletions test/cctest/test-macro-assembler-riscv64.cc
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,9 @@ TEST(LoadAddress) {
RelocInfo::INTERNAL_REFERENCE_ENCODED),
ADDRESS_LOAD);
int check_size = masm.InstructionsGeneratedSince(&skip);
// NOTE (RISCV): current li generates 8 instructions, if the sequence is
// NOTE (RISCV): current li generates 6 instructions, if the sequence is
// changed, need to adjust the CHECK_EQ value too
CHECK_EQ(8, check_size);
CHECK_EQ(6, check_size);
__ jr(a4);
__ nop();
__ stop();
Expand Down

0 comments on commit 5f073e8

Please sign in to comment.