These special-purpose arguments and return values are only relevant for the function being compiled, so add a `current` flag to legalize_signature(). - Add the necessary argument values to the entry block to represent the special-purpose arguments. - Propagate the link and sret arguments to return instructions if the legalized signature asks for it.
234 lines
7.5 KiB
Rust
234 lines
7.5 KiB
Rust
//! RISC-V Instruction Set Architecture.
|
|
|
|
pub mod settings;
|
|
mod abi;
|
|
mod binemit;
|
|
mod enc_tables;
|
|
mod registers;
|
|
|
|
use super::super::settings as shared_settings;
|
|
use binemit::CodeSink;
|
|
use isa::enc_tables::{self as shared_enc_tables, lookup_enclist, general_encoding};
|
|
use isa::Builder as IsaBuilder;
|
|
use isa::{TargetIsa, RegInfo, EncInfo, Encoding, Legalize};
|
|
use ir::{Function, Inst, InstructionData, DataFlowGraph, Signature, Type};
|
|
|
|
#[allow(dead_code)]
|
|
struct Isa {
|
|
shared_flags: shared_settings::Flags,
|
|
isa_flags: settings::Flags,
|
|
cpumode: &'static [shared_enc_tables::Level1Entry<u16>],
|
|
}
|
|
|
|
/// Get an ISA builder for creating RISC-V targets.
|
|
pub fn isa_builder() -> IsaBuilder {
|
|
IsaBuilder {
|
|
setup: settings::builder(),
|
|
constructor: isa_constructor,
|
|
}
|
|
}
|
|
|
|
fn isa_constructor(shared_flags: shared_settings::Flags,
|
|
builder: &shared_settings::Builder)
|
|
-> Box<TargetIsa> {
|
|
let level1 = if shared_flags.is_64bit() {
|
|
&enc_tables::LEVEL1_RV64[..]
|
|
} else {
|
|
&enc_tables::LEVEL1_RV32[..]
|
|
};
|
|
Box::new(Isa {
|
|
isa_flags: settings::Flags::new(&shared_flags, builder),
|
|
shared_flags: shared_flags,
|
|
cpumode: level1,
|
|
})
|
|
}
|
|
|
|
impl TargetIsa for Isa {
|
|
fn name(&self) -> &'static str {
|
|
"riscv"
|
|
}
|
|
|
|
fn flags(&self) -> &shared_settings::Flags {
|
|
&self.shared_flags
|
|
}
|
|
|
|
fn register_info(&self) -> RegInfo {
|
|
registers::INFO.clone()
|
|
}
|
|
|
|
fn encoding_info(&self) -> EncInfo {
|
|
enc_tables::INFO.clone()
|
|
}
|
|
|
|
fn encode(&self,
|
|
_dfg: &DataFlowGraph,
|
|
inst: &InstructionData,
|
|
ctrl_typevar: Type)
|
|
-> Result<Encoding, Legalize> {
|
|
lookup_enclist(ctrl_typevar,
|
|
inst.opcode(),
|
|
self.cpumode,
|
|
&enc_tables::LEVEL2[..])
|
|
.and_then(|enclist_offset| {
|
|
general_encoding(enclist_offset,
|
|
&enc_tables::ENCLISTS[..],
|
|
|instp| enc_tables::check_instp(inst, instp),
|
|
|isap| self.isa_flags.numbered_predicate(isap as usize))
|
|
.ok_or(Legalize::Expand)
|
|
})
|
|
}
|
|
|
|
fn legalize_signature(&self, sig: &mut Signature, current: bool) {
|
|
// We can pass in `self.isa_flags` too, if we need it.
|
|
abi::legalize_signature(sig, &self.shared_flags, current)
|
|
}
|
|
|
|
fn emit_inst(&self, func: &Function, inst: Inst, sink: &mut CodeSink) {
|
|
binemit::emit_inst(func, inst, sink)
|
|
}
|
|
|
|
fn reloc_names(&self) -> &'static [&'static str] {
|
|
&binemit::RELOC_NAMES
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use settings::{self, Configurable};
|
|
use isa;
|
|
use ir::{DataFlowGraph, InstructionData, Opcode};
|
|
use ir::{types, immediates};
|
|
|
|
fn encstr(isa: &isa::TargetIsa, enc: isa::Encoding) -> String {
|
|
isa.encoding_info().display(enc).to_string()
|
|
}
|
|
|
|
#[test]
|
|
fn test_64bitenc() {
|
|
let mut shared_builder = settings::builder();
|
|
shared_builder.set_bool("is_64bit", true).unwrap();
|
|
let shared_flags = settings::Flags::new(&shared_builder);
|
|
let isa = isa::lookup("riscv").unwrap().finish(shared_flags);
|
|
|
|
let mut dfg = DataFlowGraph::new();
|
|
let ebb = dfg.make_ebb();
|
|
let arg64 = dfg.append_ebb_arg(ebb, types::I64);
|
|
let arg32 = dfg.append_ebb_arg(ebb, types::I32);
|
|
|
|
// Try to encode iadd_imm.i64 v1, -10.
|
|
let inst64 = InstructionData::BinaryImm {
|
|
opcode: Opcode::IaddImm,
|
|
arg: arg64,
|
|
imm: immediates::Imm64::new(-10),
|
|
};
|
|
|
|
// ADDI is I/0b00100
|
|
assert_eq!(encstr(&*isa, isa.encode(&dfg, &inst64, types::I64).unwrap()),
|
|
"I#04");
|
|
|
|
// Try to encode iadd_imm.i64 v1, -10000.
|
|
let inst64_large = InstructionData::BinaryImm {
|
|
opcode: Opcode::IaddImm,
|
|
arg: arg64,
|
|
imm: immediates::Imm64::new(-10000),
|
|
};
|
|
|
|
// Immediate is out of range for ADDI.
|
|
assert_eq!(isa.encode(&dfg, &inst64_large, types::I64),
|
|
Err(isa::Legalize::Expand));
|
|
|
|
// Create an iadd_imm.i32 which is encodable in RV64.
|
|
let inst32 = InstructionData::BinaryImm {
|
|
opcode: Opcode::IaddImm,
|
|
arg: arg32,
|
|
imm: immediates::Imm64::new(10),
|
|
};
|
|
|
|
// ADDIW is I/0b00110
|
|
assert_eq!(encstr(&*isa, isa.encode(&dfg, &inst32, types::I32).unwrap()),
|
|
"I#06");
|
|
}
|
|
|
|
// Same as above, but for RV32.
|
|
#[test]
|
|
fn test_32bitenc() {
|
|
let mut shared_builder = settings::builder();
|
|
shared_builder.set_bool("is_64bit", false).unwrap();
|
|
let shared_flags = settings::Flags::new(&shared_builder);
|
|
let isa = isa::lookup("riscv").unwrap().finish(shared_flags);
|
|
|
|
let mut dfg = DataFlowGraph::new();
|
|
let ebb = dfg.make_ebb();
|
|
let arg64 = dfg.append_ebb_arg(ebb, types::I64);
|
|
let arg32 = dfg.append_ebb_arg(ebb, types::I32);
|
|
|
|
// Try to encode iadd_imm.i64 v1, -10.
|
|
let inst64 = InstructionData::BinaryImm {
|
|
opcode: Opcode::IaddImm,
|
|
arg: arg64,
|
|
imm: immediates::Imm64::new(-10),
|
|
};
|
|
|
|
// In 32-bit mode, an i64 bit add should be narrowed.
|
|
assert_eq!(isa.encode(&dfg, &inst64, types::I64),
|
|
Err(isa::Legalize::Narrow));
|
|
|
|
// Try to encode iadd_imm.i64 v1, -10000.
|
|
let inst64_large = InstructionData::BinaryImm {
|
|
opcode: Opcode::IaddImm,
|
|
arg: arg64,
|
|
imm: immediates::Imm64::new(-10000),
|
|
};
|
|
|
|
// In 32-bit mode, an i64 bit add should be narrowed.
|
|
assert_eq!(isa.encode(&dfg, &inst64_large, types::I64),
|
|
Err(isa::Legalize::Narrow));
|
|
|
|
// Create an iadd_imm.i32 which is encodable in RV32.
|
|
let inst32 = InstructionData::BinaryImm {
|
|
opcode: Opcode::IaddImm,
|
|
arg: arg32,
|
|
imm: immediates::Imm64::new(10),
|
|
};
|
|
|
|
// ADDI is I/0b00100
|
|
assert_eq!(encstr(&*isa, isa.encode(&dfg, &inst32, types::I32).unwrap()),
|
|
"I#04");
|
|
|
|
// Create an imul.i32 which is encodable in RV32, but only when use_m is true.
|
|
let mul32 = InstructionData::Binary {
|
|
opcode: Opcode::Imul,
|
|
args: [arg32, arg32],
|
|
};
|
|
|
|
assert_eq!(isa.encode(&dfg, &mul32, types::I32),
|
|
Err(isa::Legalize::Expand));
|
|
}
|
|
|
|
#[test]
|
|
fn test_rv32m() {
|
|
let mut shared_builder = settings::builder();
|
|
shared_builder.set_bool("is_64bit", false).unwrap();
|
|
let shared_flags = settings::Flags::new(&shared_builder);
|
|
|
|
// Set the supports_m stting which in turn enables the use_m predicate that unlocks
|
|
// encodings for imul.
|
|
let mut isa_builder = isa::lookup("riscv").unwrap();
|
|
isa_builder.set_bool("supports_m", true).unwrap();
|
|
|
|
let isa = isa_builder.finish(shared_flags);
|
|
|
|
let mut dfg = DataFlowGraph::new();
|
|
let ebb = dfg.make_ebb();
|
|
let arg32 = dfg.append_ebb_arg(ebb, types::I32);
|
|
|
|
// Create an imul.i32 which is encodable in RV32M.
|
|
let mul32 = InstructionData::Binary {
|
|
opcode: Opcode::Imul,
|
|
args: [arg32, arg32],
|
|
};
|
|
assert_eq!(encstr(&*isa, isa.encode(&dfg, &mul32, types::I32).unwrap()),
|
|
"R#10c");
|
|
}
|
|
}
|