[meta] Port Formats and Operands to the Rust crate;

This commit is contained in:
Benjamin Bouvier
2019-03-11 19:25:11 +01:00
parent 146e0bd2f5
commit d59bef1902
7 changed files with 978 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
use crate::cdsl::operands::{OperandKind, OperandKindBuilder as Builder, OperandKindFields};
/// Small helper to initialize an OperandBuilder with the right kind, for a given name and doc.
fn create(name: &'static str, doc: &'static str) -> Builder {
Builder::new(name, OperandKindFields::EntityRef).doc(doc)
}
pub fn define() -> Vec<OperandKind> {
let mut kinds = Vec::new();
// A reference to an extended basic block in the same function.
// This is primarliy used in control flow instructions.
let ebb = create("ebb", "An extended basic block in the same function.")
.default_member("destination")
.finish();
kinds.push(ebb);
// A reference to a stack slot declared in the function preamble.
let stack_slot = create("stack_slot", "A stack slot").finish();
kinds.push(stack_slot);
// A reference to a global value.
let global_value = create("global_value", "A global value.").finish();
kinds.push(global_value);
// A reference to a function signature declared in the function preamble.
// This is used to provide the call signature in a call_indirect instruction.
let sig_ref = create("sig_ref", "A function signature.").finish();
kinds.push(sig_ref);
// A reference to an external function declared in the function preamble.
// This is used to provide the callee and signature in a call instruction.
let func_ref = create("func_ref", "An external function.").finish();
kinds.push(func_ref);
// A reference to a jump table declared in the function preamble.
let jump_table = create("jump_table", "A jump table.")
.default_member("table")
.finish();
kinds.push(jump_table);
// A reference to a heap declared in the function preamble.
let heap = create("heap", "A heap.").finish();
kinds.push(heap);
// A reference to a table declared in the function preamble.
let table = create("table", "A table.").finish();
kinds.push(table);
// A variable-sized list of value operands. Use for Ebb and function call arguments.
let varargs = Builder::new("variable_args", OperandKindFields::VariableArgs)
.doc(
r#"
A variable size list of `value` operands.
Use this to represent arguments passed to a function call, arguments
passed to an extended basic block, or a variable number of results
returned from an instruction.
"#,
)
.finish();
kinds.push(varargs);
return kinds;
}

View File

@@ -0,0 +1,184 @@
use crate::cdsl::formats::{FormatRegistry, InstructionFormatBuilder as Builder};
use crate::shared::OperandKinds;
pub fn define(immediates: &OperandKinds, entities: &OperandKinds) -> FormatRegistry {
// Shorthands for immediates.
let uimm8 = immediates.by_name("uimm8");
let uimm32 = immediates.by_name("uimm32");
let imm64 = immediates.by_name("imm64");
let ieee32 = immediates.by_name("ieee32");
let ieee64 = immediates.by_name("ieee64");
let boolean = immediates.by_name("boolean");
let intcc = immediates.by_name("intcc");
let floatcc = immediates.by_name("floatcc");
let memflags = immediates.by_name("memflags");
let offset32 = immediates.by_name("offset32");
let trapcode = immediates.by_name("trapcode");
let regunit = immediates.by_name("regunit");
// Shorthands for entities.
let global_value = entities.by_name("global_value");
let ebb = entities.by_name("ebb");
let jump_table = entities.by_name("jump_table");
let func_ref = entities.by_name("func_ref");
let sig_ref = entities.by_name("sig_ref");
let stack_slot = entities.by_name("stack_slot");
let heap = entities.by_name("heap");
let table = entities.by_name("table");
let mut registry = FormatRegistry::new();
registry.insert(Builder::new("Unary").value());
registry.insert(Builder::new("UnaryImm").imm(imm64));
registry.insert(Builder::new("UnaryIeee32").imm(ieee32));
registry.insert(Builder::new("UnaryIeee64").imm(ieee64));
registry.insert(Builder::new("UnaryBool").imm(boolean));
registry.insert(Builder::new("UnaryGlobalValue").imm(global_value));
registry.insert(Builder::new("Binary").value().value());
registry.insert(Builder::new("BinaryImm").value().imm(imm64));
// The select instructions are controlled by the second VALUE operand.
// The first VALUE operand is the controlling flag which has a derived type.
// The fma instruction has the same constraint on all inputs.
registry.insert(
Builder::new("Ternary")
.value()
.value()
.value()
.typevar_operand(1),
);
// Catch-all for instructions with many outputs and inputs and no immediate
// operands.
registry.insert(Builder::new("MultiAry").varargs());
registry.insert(Builder::new("NullAry"));
registry.insert(
Builder::new("InsertLane")
.value()
.imm(("lane", uimm8))
.value(),
);
registry.insert(Builder::new("ExtractLane").value().imm(("lane", uimm8)));
registry.insert(Builder::new("IntCompare").imm(intcc).value().value());
registry.insert(Builder::new("IntCompareImm").imm(intcc).value().imm(imm64));
registry.insert(Builder::new("IntCond").imm(intcc).value());
registry.insert(Builder::new("FloatCompare").imm(floatcc).value().value());
registry.insert(Builder::new("FloatCond").imm(floatcc).value());;
registry.insert(Builder::new("IntSelect").imm(intcc).value().value().value());
registry.insert(Builder::new("Jump").imm(ebb).varargs());
registry.insert(Builder::new("Branch").value().imm(ebb).varargs());
registry.insert(
Builder::new("BranchInt")
.imm(intcc)
.value()
.imm(ebb)
.varargs(),
);
registry.insert(
Builder::new("BranchFloat")
.imm(floatcc)
.value()
.imm(ebb)
.varargs(),
);
registry.insert(
Builder::new("BranchIcmp")
.imm(intcc)
.value()
.value()
.imm(ebb)
.varargs(),
);
registry.insert(Builder::new("BranchTable").value().imm(ebb).imm(jump_table));
registry.insert(
Builder::new("BranchTableEntry")
.value()
.value()
.imm(uimm8)
.imm(jump_table),
);
registry.insert(Builder::new("BranchTableBase").imm(jump_table));
registry.insert(Builder::new("IndirectJump").value().imm(jump_table));
registry.insert(Builder::new("Call").imm(func_ref).varargs());
registry.insert(Builder::new("CallIndirect").imm(sig_ref).value().varargs());
registry.insert(Builder::new("FuncAddr").imm(func_ref));
registry.insert(Builder::new("Load").imm(memflags).value().imm(offset32));
registry.insert(
Builder::new("LoadComplex")
.imm(memflags)
.varargs()
.imm(offset32),
);
registry.insert(
Builder::new("Store")
.imm(memflags)
.value()
.value()
.imm(offset32),
);
registry.insert(
Builder::new("StoreComplex")
.imm(memflags)
.value()
.varargs()
.imm(offset32),
);
registry.insert(Builder::new("StackLoad").imm(stack_slot).imm(offset32));
registry.insert(
Builder::new("StackStore")
.value()
.imm(stack_slot)
.imm(offset32),
);
// Accessing a WebAssembly heap.
registry.insert(Builder::new("HeapAddr").imm(heap).value().imm(uimm32));
// Accessing a WebAssembly table.
registry.insert(Builder::new("TableAddr").imm(table).value().imm(offset32));
registry.insert(
Builder::new("RegMove")
.value()
.imm(("src", regunit))
.imm(("dst", regunit)),
);
registry.insert(
Builder::new("CopySpecial")
.imm(("src", regunit))
.imm(("dst", regunit)),
);
registry.insert(
Builder::new("RegSpill")
.value()
.imm(("src", regunit))
.imm(("dst", stack_slot)),
);
registry.insert(
Builder::new("RegFill")
.value()
.imm(("src", stack_slot))
.imm(("dst", regunit)),
);
registry.insert(Builder::new("Trap").imm(trapcode));
registry.insert(Builder::new("CondTrap").value().imm(trapcode));
registry.insert(Builder::new("IntCondTrap").imm(intcc).value().imm(trapcode));
registry.insert(
Builder::new("FloatCondTrap")
.imm(floatcc)
.value()
.imm(trapcode),
);
registry
}

View File

@@ -0,0 +1,145 @@
use crate::cdsl::operands::{OperandKind, OperandKindBuilder as Builder};
use std::collections::HashMap;
pub fn define() -> Vec<OperandKind> {
let mut kinds = Vec::new();
// A 64-bit immediate integer operand.
//
// This type of immediate integer can interact with SSA values with any
// IntType type.
let imm64 = Builder::new_imm("imm64")
.doc("A 64-bit immediate integer.")
.finish();
kinds.push(imm64);
// An unsigned 8-bit immediate integer operand.
//
// This small operand is used to indicate lane indexes in SIMD vectors and
// immediate bit counts on shift instructions.
let uimm8 = Builder::new_imm("uimm8")
.doc("An 8-bit immediate unsigned integer.")
.finish();
kinds.push(uimm8);
// An unsigned 32-bit immediate integer operand.
let uimm32 = Builder::new_imm("uimm32")
.doc("A 32-bit immediate unsigned integer.")
.finish();
kinds.push(uimm32);
// A 32-bit immediate signed offset.
//
// This is used to represent an immediate address offset in load/store
// instructions.
let offset32 = Builder::new_imm("offset32")
.doc("A 32-bit immediate signed offset.")
.default_member("offset")
.finish();
kinds.push(offset32);
// A 32-bit immediate floating point operand.
//
// IEEE 754-2008 binary32 interchange format.
let ieee32 = Builder::new_imm("ieee32")
.doc("A 32-bit immediate floating point number.")
.finish();
kinds.push(ieee32);
// A 64-bit immediate floating point operand.
//
// IEEE 754-2008 binary64 interchange format.
let ieee64 = Builder::new_imm("ieee64")
.doc("A 64-bit immediate floating point number.")
.finish();
kinds.push(ieee64);
// An immediate boolean operand.
//
// This type of immediate boolean can interact with SSA values with any
// BoolType type.
let boolean = Builder::new_imm("boolean")
.doc("An immediate boolean.")
.rust_type("bool")
.finish();
kinds.push(boolean);
// A condition code for comparing integer values.
// This enumerated operand kind is used for the `icmp` instruction and corresponds to the
// condcodes::IntCC` Rust type.
let mut intcc_values = HashMap::new();
intcc_values.insert("eq", "Equal");
intcc_values.insert("ne", "NotEqual");
intcc_values.insert("sge", "UnsignedGreaterThanOrEqual");
intcc_values.insert("sgt", "UnsignedGreaterThan");
intcc_values.insert("sle", "UnsignedLessThanOrEqual");
intcc_values.insert("slt", "UnsignedLessThan");
intcc_values.insert("uge", "UnsignedGreaterThanOrEqual");
intcc_values.insert("ugt", "UnsignedGreaterThan");
intcc_values.insert("ule", "UnsignedLessThanOrEqual");
intcc_values.insert("ult", "UnsignedLessThan");
let intcc = Builder::new_enum("intcc", intcc_values)
.doc("An integer comparison condition code.")
.default_member("cond")
.rust_type("ir::condcodes::IntCC")
.finish();
kinds.push(intcc);
// A condition code for comparing floating point values. This enumerated operand kind is used
// for the `fcmp` instruction and corresponds to the `condcodes::FloatCC` Rust type.
let mut floatcc_values = HashMap::new();
floatcc_values.insert("ord", "Ordered");
floatcc_values.insert("uno", "Unordered");
floatcc_values.insert("eq", "Equal");
floatcc_values.insert("ne", "NotEqual");
floatcc_values.insert("one", "OrderedNotEqual");
floatcc_values.insert("ueq", "UnorderedOrEqual");
floatcc_values.insert("lt", "LessThan");
floatcc_values.insert("le", "LessThanOrEqual");
floatcc_values.insert("gt", "GreaterThan");
floatcc_values.insert("ge", "GreaterThanOrEqual");
floatcc_values.insert("ult", "UnorderedOrLessThan");
floatcc_values.insert("ule", "UnorderedOrLessThanOrEqual");
floatcc_values.insert("ugt", "UnorderedOrGreaterThan");
floatcc_values.insert("uge", "UnorderedOrGreaterThanOrEqual");
let floatcc = Builder::new_enum("floatcc", floatcc_values)
.doc("A floating point comparison condition code")
.default_member("cond")
.rust_type("ir::condcodes::FloatCC")
.finish();
kinds.push(floatcc);
// Flags for memory operations like :clif:inst:`load` and :clif:inst:`store`.
let memflags = Builder::new_imm("memflags")
.doc("Memory operation flags")
.default_member("flags")
.rust_type("ir::MemFlags")
.finish();
kinds.push(memflags);
// A register unit in the current target ISA.
let regunit = Builder::new_imm("regunit")
.doc("A register unit in the target ISA")
.rust_type("isa::RegUnit")
.finish();
kinds.push(regunit);
// A trap code indicating the reason for trapping.
//
// The Rust enum type also has a `User(u16)` variant for user-provided trap
// codes.
let mut trapcode_values = HashMap::new();
trapcode_values.insert("stk_ovf", "StackOverflow");
trapcode_values.insert("heap_oob", "HeapOutOfBounds");
trapcode_values.insert("int_ovf", "IntegerOverflow");
trapcode_values.insert("int_divz", "IntegerDivisionByZero");
let trapcode = Builder::new_enum("trapcode", trapcode_values)
.doc("A trap reason code.")
.default_member("code")
.rust_type("ir::TrapCode")
.finish();
kinds.push(trapcode);
return kinds;
}

View File

@@ -1,4 +1,55 @@
//! Shared definitions for the Cranelift intermediate language.
pub mod entities;
pub mod formats;
pub mod immediates;
pub mod settings;
pub mod types;
use crate::cdsl::formats::FormatRegistry;
use crate::cdsl::operands::OperandKind;
use crate::cdsl::settings::SettingGroup;
pub struct Definitions {
pub settings: SettingGroup,
pub operand_kinds: OperandKinds,
pub format_registry: FormatRegistry,
}
pub struct OperandKinds(Vec<OperandKind>);
impl OperandKinds {
pub fn new() -> Self {
Self(Vec::new())
}
pub fn by_name(&self, name: &'static str) -> &OperandKind {
self.0
.iter()
.find(|op| op.name == name)
.expect(&format!("unknown Operand name: {}", name))
}
pub fn push(&mut self, operand_kind: OperandKind) {
assert!(
self.0
.iter()
.find(|existing| existing.name == operand_kind.name)
.is_none(),
"trying to insert operand kind '{}' for the second time",
operand_kind.name
);
self.0.push(operand_kind);
}
}
pub fn define() -> Definitions {
let immediates = OperandKinds(immediates::define());
let entities = OperandKinds(entities::define());
let format_registry = formats::define(&immediates, &entities);
Definitions {
settings: settings::define(),
operand_kinds: immediates,
format_registry,
}
}