Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added li_ptr to create 48 bit immediate addresses. #272

Merged
1 commit merged into from
Oct 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -2362,6 +2365,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 @@ -2646,52 +2667,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 @@ -2702,59 +2717,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 @@ -625,6 +625,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 @@ -836,6 +837,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);
This conversation was marked as resolved.
Show resolved Hide resolved
} 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
This conversation was marked as resolved.
Show resolved Hide resolved
// 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 @@ -1319,6 +1319,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 @@ -1449,35 +1450,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