///|
priv enum ValueDefinition {
FunctionParameter
BlockParameter(Block)
InstructionResult(Instruction)
}
///|
priv struct ValueData {
ty : ValueType
definition : ValueDefinition
}
///|
priv struct BlockData {
parameters : Array[Value]
body : Array[Instruction]
mut terminator : Instruction?
}
///|
priv enum InstructionRole {
Body
Terminator
} derive(Eq)
///|
priv struct InstructionData[Inst] {
inst : Inst
parent : Block
role : InstructionRole
operand_start : Int
operand_count : Int
clobber_start : Int
clobber_count : Int
results : Array[Value]
successors : Array[Edge]
metadata : InstructionMetadata
}
///|
pub struct Function[Inst] {
priv owner : Ref[Unit]
priv name : String
priv protocol : @semantic.CallProtocol
priv parameters : Array[Value]
priv result_types : Array[ValueType]
priv values : Array[ValueData]
priv blocks : Array[BlockData]
priv instructions : Array[InstructionData[Inst]]
priv operands : Array[OperandData]
priv clobbers : Array[PhysicalReg]
priv mut layout : Array[Block]
}
///|
fn[Inst] Function::new(
name : String,
protocol : @semantic.CallProtocol,
parameter_types : Array[ValueType],
result_types : Array[ValueType],
) -> Function[Inst] {
let function : Function[Inst] = {
owner: Ref(()),
name,
protocol,
parameters: [],
result_types: result_types.copy(),
values: [],
blocks: [],
instructions: [],
operands: [],
clobbers: [],
layout: [],
}
for ty in parameter_types {
let value = function.allocate_value(ty, FunctionParameter)
function.parameters.push(value)
}
let entry = function.allocate_block([])
function.layout.push(entry)
function
}
///|
pub fn[Inst] Function::protocol(
self : Function[Inst],
) -> @semantic.CallProtocol {
self.protocol
}
///|
pub fn[Inst] Function::result_types(self : Function[Inst]) -> Array[ValueType] {
self.result_types.copy()
}
///|
fn[Inst] Function::allocate_value(
self : Function[Inst],
ty : ValueType,
definition : ValueDefinition,
) -> Value {
let value = Value::new(self.owner, self.values.length())
self.values.push({ ty, definition })
value
}
///|
fn[Inst] Function::allocate_block(
self : Function[Inst],
parameter_types : Array[ValueType],
) -> Block {
let block = Block::new(self.owner, self.blocks.length())
let parameters : Array[Value] = []
for ty in parameter_types {
parameters.push(self.allocate_value(ty, BlockParameter(block)))
}
self.blocks.push({ parameters, body: [], terminator: None })
block
}
///|
fn[Inst] Function::owns_value(self : Function[Inst], value : Value) -> Bool {
physical_equal(self.owner, value.owner) &&
value.id >= 0 &&
value.id < self.values.length()
}
///|
fn[Inst] Function::owns_block(self : Function[Inst], block : Block) -> Bool {
physical_equal(self.owner, block.owner) &&
block.id >= 0 &&
block.id < self.blocks.length()
}
///|
fn[Inst] Function::owns_instruction(
self : Function[Inst],
instruction : Instruction,
) -> Bool {
physical_equal(self.owner, instruction.owner) &&
instruction.id >= 0 &&
instruction.id < self.instructions.length()
}
///|
pub fn[Inst] Function::name(self : Function[Inst]) -> String {
self.name
}
///|
pub fn[Inst] Function::entry_block(self : Function[Inst]) -> Block {
Block::new(self.owner, 0)
}
///|
pub fn[Inst] Function::parameter_count(self : Function[Inst]) -> Int {
self.parameters.length()
}
///|
pub fn[Inst] Function::parameter_at(
self : Function[Inst],
index : Int,
) -> Value? {
self.parameters.get(index)
}
///|
pub fn[Inst] Function::value_count(self : Function[Inst]) -> Int {
self.values.length()
}
///|
pub fn[Inst] Function::value_at(self : Function[Inst], index : Int) -> Value? {
if index >= 0 && index < self.values.length() {
Some(Value::new(self.owner, index))
} else {
None
}
}
///|
pub fn[Inst] Function::value_index(
self : Function[Inst],
value : Value,
) -> Int? {
if self.owns_value(value) {
Some(value.id)
} else {
None
}
}
///|
pub fn[Inst] Function::value_type(
self : Function[Inst],
value : Value,
) -> ValueType? {
if self.owns_value(value) {
Some(self.values[value.id].ty)
} else {
None
}
}
///|
pub fn[Inst] Function::block_count(self : Function[Inst]) -> Int {
self.blocks.length()
}
///|
pub fn[Inst] Function::block_at(self : Function[Inst], index : Int) -> Block? {
if index >= 0 && index < self.blocks.length() {
Some(Block::new(self.owner, index))
} else {
None
}
}
///|
pub fn[Inst] Function::block_index(
self : Function[Inst],
block : Block,
) -> Int? {
if self.owns_block(block) {
Some(block.id)
} else {
None
}
}
///|
pub fn[Inst] Function::block_parameters(
self : Function[Inst],
block : Block,
) -> Array[Value] {
if self.owns_block(block) {
self.blocks[block.id].parameters.copy()
} else {
[]
}
}
///|
pub fn[Inst] Function::block_body(
self : Function[Inst],
block : Block,
) -> Array[Instruction] {
if self.owns_block(block) {
self.blocks[block.id].body.copy()
} else {
[]
}
}
///|
pub fn[Inst] Function::block_terminator(
self : Function[Inst],
block : Block,
) -> Instruction? {
if self.owns_block(block) {
self.blocks[block.id].terminator
} else {
None
}
}
///|
pub fn[Inst] Function::instruction_count(self : Function[Inst]) -> Int {
self.instructions.length()
}
///|
pub fn[Inst] Function::instruction_at(
self : Function[Inst],
index : Int,
) -> Instruction? {
if index >= 0 && index < self.instructions.length() {
Some(Instruction::new(self.owner, index))
} else {
None
}
}
///|
pub fn[Inst] Function::instruction_index(
self : Function[Inst],
instruction : Instruction,
) -> Int? {
if self.owns_instruction(instruction) {
Some(instruction.id)
} else {
None
}
}
///|
pub fn[Inst] Function::instruction(
self : Function[Inst],
instruction : Instruction,
) -> Inst? {
if self.owns_instruction(instruction) {
Some(self.instructions[instruction.id].inst)
} else {
None
}
}
///|
pub fn[Inst] Function::instruction_operands(
self : Function[Inst],
instruction : Instruction,
) -> Array[Operand] {
if !self.owns_instruction(instruction) {
return []
}
let data = self.instructions[instruction.id]
Array::makei(data.operand_count, index => {
self.operands[data.operand_start + index].snapshot()
})
}
///|
pub fn[Inst] Function::instruction_operand_count(
self : Function[Inst],
instruction : Instruction,
) -> Int {
if self.owns_instruction(instruction) {
self.instructions[instruction.id].operand_count
} else {
0
}
}
///|
pub fn[Inst] Function::instruction_operand_at(
self : Function[Inst],
instruction : Instruction,
index : Int,
) -> Operand? {
if !self.owns_instruction(instruction) {
return None
}
let data = self.instructions[instruction.id]
if index < 0 || index >= data.operand_count {
return None
}
Some(self.operands[data.operand_start + index].snapshot())
}
///|
pub fn[Inst] Function::instruction_clobbers(
self : Function[Inst],
instruction : Instruction,
) -> Array[PhysicalReg] {
if !self.owns_instruction(instruction) {
return []
}
let data = self.instructions[instruction.id]
self.clobbers[data.clobber_start:data.clobber_start + data.clobber_count].to_owned()
}
///|
pub fn[Inst] Function::instruction_clobber_count(
self : Function[Inst],
instruction : Instruction,
) -> Int {
if self.owns_instruction(instruction) {
self.instructions[instruction.id].clobber_count
} else {
0
}
}
///|
pub fn[Inst] Function::instruction_clobber_at(
self : Function[Inst],
instruction : Instruction,
index : Int,
) -> PhysicalReg? {
if !self.owns_instruction(instruction) {
return None
}
let data = self.instructions[instruction.id]
if index < 0 || index >= data.clobber_count {
return None
}
self.clobbers.get(data.clobber_start + index)
}
///|
pub fn[Inst] Function::instruction_results(
self : Function[Inst],
instruction : Instruction,
) -> Array[Value] {
if self.owns_instruction(instruction) {
self.instructions[instruction.id].results.copy()
} else {
[]
}
}
///|
pub fn[Inst] Function::instruction_is_terminator(
self : Function[Inst],
instruction : Instruction,
) -> Bool {
self.owns_instruction(instruction) &&
self.instructions[instruction.id].role == Terminator
}
///|
pub fn[Inst] Function::instruction_successors(
self : Function[Inst],
instruction : Instruction,
) -> Array[Edge] {
if self.owns_instruction(instruction) {
self.instructions[instruction.id].successors.map(edge => edge.copy())
} else {
[]
}
}
///|
pub fn[Inst] Function::instruction_successor_count(
self : Function[Inst],
instruction : Instruction,
) -> Int {
if self.owns_instruction(instruction) {
self.instructions[instruction.id].successors.length()
} else {
0
}
}
///|
pub fn[Inst] Function::instruction_successor_at(
self : Function[Inst],
instruction : Instruction,
index : Int,
) -> Edge? {
if !self.owns_instruction(instruction) {
return None
}
match self.instructions[instruction.id].successors.get(index) {
Some(edge) => Some(edge.copy())
None => None
}
}
///|
pub fn[Inst] Function::instruction_metadata(
self : Function[Inst],
instruction : Instruction,
) -> InstructionMetadata? {
if self.owns_instruction(instruction) {
Some(self.instructions[instruction.id].metadata.copy())
} else {
None
}
}
///|
pub fn[Inst] Function::layout(self : Function[Inst]) -> Array[Block] {
self.layout.copy()
}
///|
pub fn[Inst] Function::set_layout(
self : Function[Inst],
layout : Array[Block],
) -> Unit raise VCodeBuildError {
if layout.length() != self.blocks.length() {
raise InvalidLayout
}
let seen = Array::make(self.blocks.length(), false)
for block in layout {
if !self.owns_block(block) || seen[block.id] {
raise InvalidLayout
}
seen[block.id] = true
}
self.layout = layout.copy()
}
///|
pub fn[Inst] Function::before(
self : Function[Inst],
instruction : Instruction,
) -> ProgramPoint? {
if self.owns_instruction(instruction) {
Some(ProgramPoint::new(self.owner, instruction, Before))
} else {
None
}
}
///|
pub fn[Inst] Function::after(
self : Function[Inst],
instruction : Instruction,
) -> ProgramPoint? {
if self.owns_instruction(instruction) {
Some(ProgramPoint::new(self.owner, instruction, After))
} else {
None
}
}
///|
pub fn[Inst] Function::program_point_instruction(
self : Function[Inst],
point : ProgramPoint,
) -> Instruction? {
if physical_equal(self.owner, point.owner) &&
self.owns_instruction(point.instruction) {
Some(point.instruction)
} else {
None
}
}