388 lines
7.3 KiB
V
388 lines
7.3 KiB
V
module mir
|
|
|
|
import v3.ssa
|
|
|
|
pub enum TargetArch {
|
|
unknown
|
|
arm64
|
|
}
|
|
|
|
pub enum AbiKind {
|
|
unknown
|
|
aapcs64
|
|
}
|
|
|
|
pub struct Target {
|
|
pub:
|
|
arch TargetArch
|
|
abi AbiKind
|
|
pointer_size int
|
|
int_arg_regs int
|
|
ret_regs int
|
|
stack_align int
|
|
}
|
|
|
|
pub enum AbiLocationKind {
|
|
none
|
|
register
|
|
stack
|
|
indirect
|
|
}
|
|
|
|
pub struct AbiLocation {
|
|
pub:
|
|
kind AbiLocationKind
|
|
register_index int
|
|
register_count int
|
|
stack_offset int
|
|
size int
|
|
}
|
|
|
|
pub struct FunctionAbi {
|
|
pub:
|
|
params []AbiLocation
|
|
ret AbiLocation
|
|
stack_args_size int
|
|
stack_align int
|
|
}
|
|
|
|
pub struct Value {
|
|
pub mut:
|
|
id int
|
|
kind ssa.ValueKind
|
|
typ ssa.TypeID
|
|
name string
|
|
index int
|
|
uses []ssa.ValueID
|
|
}
|
|
|
|
pub struct Instruction {
|
|
pub mut:
|
|
op ssa.OpCode
|
|
operands []ssa.ValueID
|
|
typ ssa.TypeID
|
|
block int
|
|
}
|
|
|
|
pub struct BasicBlock {
|
|
pub mut:
|
|
id int
|
|
name string
|
|
parent int
|
|
instrs []ssa.ValueID
|
|
preds []ssa.BlockID
|
|
succs []ssa.BlockID
|
|
}
|
|
|
|
pub struct Function {
|
|
pub mut:
|
|
id int
|
|
name string
|
|
typ ssa.TypeID
|
|
blocks []ssa.BlockID
|
|
params []ssa.ValueID
|
|
is_c_extern bool
|
|
abi FunctionAbi
|
|
}
|
|
|
|
@[heap]
|
|
pub struct Module {
|
|
pub mut:
|
|
target Target
|
|
type_store ssa.TypeStore
|
|
values []Value
|
|
instrs []Instruction
|
|
blocks []BasicBlock
|
|
funcs []Function
|
|
globals []ssa.GlobalVar
|
|
}
|
|
|
|
// default_target returns a conservative target descriptor for target-neutral MIR.
|
|
pub fn default_target() Target {
|
|
return Target{
|
|
arch: .unknown
|
|
abi: .unknown
|
|
pointer_size: 8
|
|
stack_align: 8
|
|
}
|
|
}
|
|
|
|
// arm64_target returns the target descriptor used by the native ARM64 pipeline.
|
|
pub fn arm64_target() Target {
|
|
return Target{
|
|
arch: .arm64
|
|
abi: .aapcs64
|
|
pointer_size: 8
|
|
int_arg_regs: 8
|
|
ret_regs: 8
|
|
stack_align: 16
|
|
}
|
|
}
|
|
|
|
// lower_from_ssa lowers an SSA module into target-neutral MIR.
|
|
pub fn lower_from_ssa(m &ssa.Module) Module {
|
|
return lower_from_ssa_for_target(m, default_target())
|
|
}
|
|
|
|
// lower_from_ssa_for_target lowers an SSA module into MIR with target ABI metadata.
|
|
pub fn lower_from_ssa_for_target(m &ssa.Module, target Target) Module {
|
|
mut mod := Module{
|
|
target: target
|
|
type_store: m.type_store
|
|
values: []Value{len: m.values.len}
|
|
instrs: []Instruction{len: m.instrs.len}
|
|
blocks: []BasicBlock{len: m.blocks.len}
|
|
funcs: []Function{len: m.funcs.len}
|
|
globals: m.globals
|
|
}
|
|
|
|
for i, val in m.values {
|
|
mod.values[i] = Value{
|
|
id: val.id
|
|
kind: val.kind
|
|
typ: val.typ
|
|
name: val.name
|
|
index: val.index
|
|
uses: val.uses
|
|
}
|
|
}
|
|
|
|
for i, instr in m.instrs {
|
|
mod.instrs[i] = Instruction{
|
|
op: instr.op
|
|
operands: instr.operands
|
|
typ: instr.typ
|
|
block: instr.block
|
|
}
|
|
}
|
|
|
|
for i, blk in m.blocks {
|
|
mod.blocks[i] = BasicBlock{
|
|
id: blk.id
|
|
name: blk.name
|
|
parent: blk.parent
|
|
instrs: blk.instrs
|
|
preds: blk.preds
|
|
succs: blk.succs
|
|
}
|
|
}
|
|
|
|
for i, f in m.funcs {
|
|
mod.funcs[i] = Function{
|
|
id: f.id
|
|
name: f.name
|
|
typ: f.typ
|
|
blocks: f.blocks
|
|
params: f.params
|
|
is_c_extern: f.is_c_extern
|
|
}
|
|
}
|
|
|
|
for i, f in mod.funcs {
|
|
mut func := f
|
|
func.abi = mod.build_function_abi(func)
|
|
mod.funcs[i] = func
|
|
}
|
|
|
|
return mod
|
|
}
|
|
|
|
pub fn (m &Module) type_size(typ_id ssa.TypeID) int {
|
|
return m.type_size_inner(typ_id, 0)
|
|
}
|
|
|
|
fn (m &Module) type_size_inner(typ_id ssa.TypeID, depth int) int {
|
|
if typ_id <= 0 || typ_id >= m.type_store.types.len {
|
|
return 0
|
|
}
|
|
if depth > 32 {
|
|
return 8
|
|
}
|
|
typ := m.type_store.types[typ_id]
|
|
if typ.width > 0 {
|
|
return (typ.width + 7) / 8
|
|
}
|
|
if typ.elem_type > 0 && typ.fields.len == 0 {
|
|
return 8
|
|
}
|
|
if typ.fields.len == 0 {
|
|
if typ.params.len > 0 || typ.ret_type > 0 {
|
|
return 8
|
|
}
|
|
return 0
|
|
}
|
|
if typ.fields.len > 256 {
|
|
return 8
|
|
}
|
|
mut offset := 0
|
|
mut max_align := 1
|
|
for i in 0 .. typ.fields.len {
|
|
field_typ := typ.fields[i]
|
|
align := m.type_align_inner(field_typ, depth + 1)
|
|
if align > max_align {
|
|
max_align = align
|
|
}
|
|
if align > 1 && offset % align != 0 {
|
|
offset = (offset + align - 1) & ~(align - 1)
|
|
}
|
|
offset += m.type_size_inner(field_typ, depth + 1)
|
|
}
|
|
total := if max_align > 1 && offset % max_align != 0 {
|
|
(offset + max_align - 1) & ~(max_align - 1)
|
|
} else {
|
|
offset
|
|
}
|
|
if total > 0 {
|
|
return total
|
|
}
|
|
return 8
|
|
}
|
|
|
|
pub fn (m &Module) type_align(typ_id ssa.TypeID) int {
|
|
return m.type_align_inner(typ_id, 0)
|
|
}
|
|
|
|
fn (m &Module) type_align_inner(typ_id ssa.TypeID, depth int) int {
|
|
if typ_id <= 0 || typ_id >= m.type_store.types.len {
|
|
return 1
|
|
}
|
|
if depth > 32 {
|
|
return 8
|
|
}
|
|
typ := m.type_store.types[typ_id]
|
|
if typ.width > 0 {
|
|
size := (typ.width + 7) / 8
|
|
if size >= 8 {
|
|
return 8
|
|
}
|
|
if size >= 4 {
|
|
return 4
|
|
}
|
|
return 1
|
|
}
|
|
if typ.elem_type > 0 && typ.fields.len == 0 {
|
|
return 8
|
|
}
|
|
if typ.fields.len > 0 {
|
|
if typ.fields.len > 256 {
|
|
return 8
|
|
}
|
|
mut max_align := 1
|
|
for i in 0 .. typ.fields.len {
|
|
field_typ := typ.fields[i]
|
|
a := m.type_align_inner(field_typ, depth + 1)
|
|
if a > max_align {
|
|
max_align = a
|
|
}
|
|
}
|
|
return max_align
|
|
}
|
|
if typ.params.len > 0 || typ.ret_type > 0 {
|
|
return 8
|
|
}
|
|
size := m.type_size_inner(typ_id, depth + 1)
|
|
if size >= 8 {
|
|
return 8
|
|
}
|
|
if size >= 4 {
|
|
return 4
|
|
}
|
|
return 1
|
|
}
|
|
|
|
fn (m &Module) build_function_abi(f Function) FunctionAbi {
|
|
mut params := []AbiLocation{}
|
|
mut next_reg := 0
|
|
mut next_stack := 0
|
|
word_size := m.target_word_size()
|
|
for pid in f.params {
|
|
size := m.value_size(pid)
|
|
n_words := words_for(size, word_size)
|
|
if m.target.int_arg_regs > 0 && next_reg + n_words <= m.target.int_arg_regs {
|
|
params << AbiLocation{
|
|
kind: .register
|
|
register_index: next_reg
|
|
register_count: n_words
|
|
size: size
|
|
}
|
|
next_reg += n_words
|
|
continue
|
|
}
|
|
stack_offset := align_to(next_stack, word_size)
|
|
params << AbiLocation{
|
|
kind: .stack
|
|
register_count: n_words
|
|
stack_offset: stack_offset
|
|
size: size
|
|
}
|
|
next_stack = stack_offset + align_to(size, word_size)
|
|
}
|
|
|
|
ret_size := m.type_size(f.typ)
|
|
ret_words := words_for(ret_size, word_size)
|
|
ret := if ret_size == 0 {
|
|
AbiLocation{
|
|
kind: .none
|
|
}
|
|
} else if m.target.ret_regs > 0 && ret_words <= m.target.ret_regs {
|
|
AbiLocation{
|
|
kind: .register
|
|
register_index: 0
|
|
register_count: ret_words
|
|
size: ret_size
|
|
}
|
|
} else {
|
|
AbiLocation{
|
|
kind: .indirect
|
|
register_index: 0
|
|
register_count: 1
|
|
size: ret_size
|
|
}
|
|
}
|
|
return FunctionAbi{
|
|
params: params
|
|
ret: ret
|
|
stack_args_size: align_to(next_stack, m.target_stack_align())
|
|
stack_align: m.target_stack_align()
|
|
}
|
|
}
|
|
|
|
fn (m &Module) value_size(val_id ssa.ValueID) int {
|
|
if val_id <= 0 || val_id >= m.values.len {
|
|
return m.target_word_size()
|
|
}
|
|
size := m.type_size(m.values[val_id].typ)
|
|
if size > 0 {
|
|
return size
|
|
}
|
|
return m.target_word_size()
|
|
}
|
|
|
|
fn (m &Module) target_word_size() int {
|
|
if m.target.pointer_size > 0 {
|
|
return m.target.pointer_size
|
|
}
|
|
return 8
|
|
}
|
|
|
|
fn (m &Module) target_stack_align() int {
|
|
if m.target.stack_align > 0 {
|
|
return m.target.stack_align
|
|
}
|
|
return m.target_word_size()
|
|
}
|
|
|
|
fn words_for(size int, word_size int) int {
|
|
if size <= 0 {
|
|
return 0
|
|
}
|
|
return (size + word_size - 1) / word_size
|
|
}
|
|
|
|
fn align_to(value int, alignment int) int {
|
|
if alignment <= 1 {
|
|
return value
|
|
}
|
|
return (value + alignment - 1) & ~(alignment - 1)
|
|
}
|