ir: 중간 표현을 정의한다
명령 12개와 종결자 4개. 함수 단위 기본 블록이고 임시값은 블록을 넘지 않아 phi 노드가 없다 -- 블록을 넘겨야 하는 값은 지역을 경유한다. 코드가 조금 더 생기지만 레지스터 할당기를 블록 단위로 유지해 준다. Ferro 타입은 여기서 사라진다. 구조체·슬라이스·옵셔널·에러 유니온이 전부 mem<N> 이고 필드는 lowering 이 계산한 바이트 오프셋이다. 모노모피제이션이 프론트엔드에서 끝나므로 IR 에 제네릭이라는 개념도 없다. 덩어리는 언제나 주소로 오간다. 크기 임계값이 없어서 ISA 마다 다른 구조체 전달 규칙을 통째로 피해간다. trap 은 이유와 줄 번호만 남기고 파일 이름 문자열은 유닛당 하나를 공유한다. IR.md 가 설명이고 ir.h/ir.c 가 그 형태다. 아직 아무도 만들지 않는다.
This commit is contained in:
+408
@@ -0,0 +1,408 @@
|
||||
#include "ir.h"
|
||||
#include <string.h>
|
||||
|
||||
void fe_ir_module_init(FeIrModule *m)
|
||||
{
|
||||
fe_arena_init(&m->arena, 16384);
|
||||
m->unit_file = "";
|
||||
m->funcs = 0;
|
||||
m->last_func = 0;
|
||||
m->globals = 0;
|
||||
m->last_global = 0;
|
||||
}
|
||||
|
||||
void fe_ir_module_destroy(FeIrModule *m)
|
||||
{
|
||||
fe_arena_destroy(&m->arena);
|
||||
m->funcs = 0;
|
||||
m->last_func = 0;
|
||||
m->globals = 0;
|
||||
m->last_global = 0;
|
||||
}
|
||||
|
||||
static void *ir_alloc(FeIrModule *m, unsigned long size)
|
||||
{
|
||||
return fe_arena_alloc(&m->arena, (size_t)size);
|
||||
}
|
||||
|
||||
FeIrFunc *fe_ir_func(FeIrModule *m, const char *name, FeIrType ret,
|
||||
unsigned long ret_size)
|
||||
{
|
||||
FeIrFunc *f = (FeIrFunc *)ir_alloc(m, sizeof(FeIrFunc));
|
||||
if (!f) return 0;
|
||||
memset(f, 0, sizeof *f);
|
||||
f->name = name;
|
||||
f->ret = ret;
|
||||
f->ret_size = ret_size;
|
||||
/* An aggregate result is written through a hidden first parameter, so the
|
||||
caller owns the storage and no size threshold has to be agreed on. */
|
||||
f->returns_by_address = ret == FE_IR_MEM;
|
||||
if (m->last_func) m->last_func->next = f;
|
||||
else m->funcs = f;
|
||||
m->last_func = f;
|
||||
return f;
|
||||
}
|
||||
|
||||
unsigned fe_ir_local(FeIrModule *m, FeIrFunc *f, FeIrType type,
|
||||
unsigned long size, unsigned align, const char *name)
|
||||
{
|
||||
if (f->local_count == f->local_capacity) {
|
||||
unsigned cap = f->local_capacity ? f->local_capacity * 2U : 8U;
|
||||
FeIrLocal *grown = (FeIrLocal *)ir_alloc(m, cap * sizeof(FeIrLocal));
|
||||
if (!grown) return 0;
|
||||
if (f->locals)
|
||||
memcpy(grown, f->locals, f->local_count * sizeof(FeIrLocal));
|
||||
f->locals = grown;
|
||||
f->local_capacity = cap;
|
||||
}
|
||||
f->locals[f->local_count].type = type;
|
||||
f->locals[f->local_count].size = size;
|
||||
f->locals[f->local_count].align = align ? align : 1U;
|
||||
f->locals[f->local_count].name = name;
|
||||
return f->local_count++;
|
||||
}
|
||||
|
||||
unsigned fe_ir_temp(FeIrFunc *f)
|
||||
{
|
||||
return f->temp_count++;
|
||||
}
|
||||
|
||||
FeIrBlock *fe_ir_block(FeIrModule *m, FeIrFunc *f)
|
||||
{
|
||||
FeIrBlock *b = (FeIrBlock *)ir_alloc(m, sizeof(FeIrBlock));
|
||||
if (!b) return 0;
|
||||
memset(b, 0, sizeof *b);
|
||||
b->id = f->block_count++;
|
||||
b->func = f;
|
||||
/* Until something says otherwise a block falls off the end, which is only
|
||||
correct for a void function; lowering always sets a real terminator. */
|
||||
b->term = FE_IR_RET;
|
||||
if (f->last) f->last->next = b;
|
||||
else f->first = b;
|
||||
f->last = b;
|
||||
return b;
|
||||
}
|
||||
|
||||
FeIrPlace fe_ir_at_local(unsigned index, long offset)
|
||||
{
|
||||
FeIrPlace p;
|
||||
p.base = FE_PLACE_LOCAL; p.index = index; p.name = 0; p.offset = offset;
|
||||
return p;
|
||||
}
|
||||
|
||||
FeIrPlace fe_ir_at_global(const char *name, long offset)
|
||||
{
|
||||
FeIrPlace p;
|
||||
p.base = FE_PLACE_GLOBAL; p.index = 0; p.name = name; p.offset = offset;
|
||||
return p;
|
||||
}
|
||||
|
||||
FeIrPlace fe_ir_at_temp(unsigned temp, long offset)
|
||||
{
|
||||
FeIrPlace p;
|
||||
p.base = FE_PLACE_TEMP; p.index = temp; p.name = 0; p.offset = offset;
|
||||
return p;
|
||||
}
|
||||
|
||||
static FeIrValue *emit(FeIrModule *m, FeIrBlock *b, FeIrOp op, FeIrType t)
|
||||
{
|
||||
FeIrValue *v = (FeIrValue *)ir_alloc(m, sizeof(FeIrValue));
|
||||
if (!v) return 0;
|
||||
memset(v, 0, sizeof *v);
|
||||
v->op = op;
|
||||
v->type = t;
|
||||
if (b->last) b->last->next = v;
|
||||
else b->first = v;
|
||||
b->last = v;
|
||||
return v;
|
||||
}
|
||||
|
||||
/* A result needs a fresh temporary, and the counter lives on the function, so
|
||||
a block carries the function it is being built in. */
|
||||
static unsigned result(FeIrModule *m, FeIrBlock *b, FeIrValue *v)
|
||||
{
|
||||
(void)m;
|
||||
v->has_dest = 1;
|
||||
v->dest = fe_ir_temp(b->func);
|
||||
return v->dest;
|
||||
}
|
||||
|
||||
unsigned fe_ir_const(FeIrModule *m, FeIrBlock *b, FeIrType t, long value)
|
||||
{
|
||||
FeIrValue *v = emit(m, b, FE_IR_CONST, t);
|
||||
if (!v) return 0;
|
||||
v->imm = value;
|
||||
return result(m, b, v);
|
||||
}
|
||||
|
||||
unsigned fe_ir_load(FeIrModule *m, FeIrBlock *b, FeIrType t, FeIrPlace p)
|
||||
{
|
||||
FeIrValue *v = emit(m, b, FE_IR_LOAD, t);
|
||||
if (!v) return 0;
|
||||
v->place = p;
|
||||
return result(m, b, v);
|
||||
}
|
||||
|
||||
void fe_ir_store(FeIrModule *m, FeIrBlock *b, FeIrPlace p, unsigned value)
|
||||
{
|
||||
FeIrValue *v = emit(m, b, FE_IR_STORE, FE_IR_VOID);
|
||||
if (!v) return;
|
||||
v->place = p;
|
||||
v->a = value;
|
||||
}
|
||||
|
||||
unsigned fe_ir_addr(FeIrModule *m, FeIrBlock *b, FeIrPlace p)
|
||||
{
|
||||
FeIrValue *v = emit(m, b, FE_IR_ADDR, FE_IR_PTR);
|
||||
if (!v) return 0;
|
||||
v->place = p;
|
||||
return result(m, b, v);
|
||||
}
|
||||
|
||||
unsigned fe_ir_binary(FeIrModule *m, FeIrBlock *b, FeIrOp op, FeIrType t,
|
||||
unsigned a, unsigned c, int is_unsigned)
|
||||
{
|
||||
FeIrValue *v;
|
||||
int is_cmp = op >= FE_IR_EQ && op <= FE_IR_GE;
|
||||
v = emit(m, b, op, is_cmp ? FE_IR_I8 : t);
|
||||
if (!v) return 0;
|
||||
v->a = a;
|
||||
v->b = c;
|
||||
v->is_unsigned = is_unsigned;
|
||||
/* A comparison reports i8 but reads its operands at `t`, so the width has
|
||||
to survive somewhere the backend can see it. */
|
||||
if (is_cmp) v->imm = (long)t;
|
||||
return result(m, b, v);
|
||||
}
|
||||
|
||||
unsigned fe_ir_cast(FeIrModule *m, FeIrBlock *b, FeIrType from, FeIrType to,
|
||||
unsigned a, int is_unsigned)
|
||||
{
|
||||
FeIrValue *v = emit(m, b, FE_IR_CAST, to);
|
||||
if (!v) return 0;
|
||||
v->a = a;
|
||||
v->imm = (long)from;
|
||||
v->is_unsigned = is_unsigned;
|
||||
return result(m, b, v);
|
||||
}
|
||||
|
||||
unsigned fe_ir_call(FeIrModule *m, FeIrBlock *b, FeIrType ret,
|
||||
const char *callee, unsigned *args, unsigned count)
|
||||
{
|
||||
FeIrValue *v = emit(m, b, FE_IR_CALL, ret);
|
||||
unsigned i;
|
||||
if (!v) return 0;
|
||||
v->callee = callee;
|
||||
v->arg_count = count;
|
||||
if (count) {
|
||||
v->args = (unsigned *)ir_alloc(m, count * sizeof(unsigned));
|
||||
if (v->args) for (i = 0; i < count; ++i) v->args[i] = args[i];
|
||||
else v->arg_count = 0;
|
||||
}
|
||||
if (ret == FE_IR_VOID) return 0;
|
||||
return result(m, b, v);
|
||||
}
|
||||
|
||||
void fe_ir_copy(FeIrModule *m, FeIrBlock *b, FeIrPlace dst, FeIrPlace src,
|
||||
unsigned long size)
|
||||
{
|
||||
FeIrValue *v = emit(m, b, FE_IR_COPY, FE_IR_VOID);
|
||||
if (!v) return;
|
||||
v->place = dst;
|
||||
v->place2 = src;
|
||||
v->imm = (long)size;
|
||||
}
|
||||
|
||||
void fe_ir_jmp(FeIrBlock *b, unsigned target)
|
||||
{
|
||||
b->term = FE_IR_JMP;
|
||||
b->target = target;
|
||||
}
|
||||
|
||||
void fe_ir_br(FeIrBlock *b, unsigned cond, unsigned t, unsigned f)
|
||||
{
|
||||
b->term = FE_IR_BR;
|
||||
b->cond = cond;
|
||||
b->target = t;
|
||||
b->target_else = f;
|
||||
}
|
||||
|
||||
void fe_ir_ret(FeIrBlock *b, unsigned value, int has_value)
|
||||
{
|
||||
b->term = FE_IR_RET;
|
||||
b->ret_value = value;
|
||||
b->has_ret_value = has_value;
|
||||
}
|
||||
|
||||
void fe_ir_trap(FeIrBlock *b, FeIrTrap reason, unsigned long line)
|
||||
{
|
||||
b->term = FE_IR_TRAP;
|
||||
b->trap = reason;
|
||||
b->trap_line = line;
|
||||
}
|
||||
|
||||
const char *fe_ir_type_name(FeIrType t)
|
||||
{
|
||||
switch (t) {
|
||||
case FE_IR_VOID: return "void";
|
||||
case FE_IR_I8: return "i8";
|
||||
case FE_IR_I16: return "i16";
|
||||
case FE_IR_I32: return "i32";
|
||||
case FE_IR_PTR: return "ptr";
|
||||
case FE_IR_MEM: return "mem";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
const char *fe_ir_op_name(FeIrOp op)
|
||||
{
|
||||
switch (op) {
|
||||
case FE_IR_CONST: return "const";
|
||||
case FE_IR_LOAD: return "load";
|
||||
case FE_IR_STORE: return "store";
|
||||
case FE_IR_ADDR: return "addr";
|
||||
case FE_IR_ADD: return "add";
|
||||
case FE_IR_SUB: return "sub";
|
||||
case FE_IR_MUL: return "mul";
|
||||
case FE_IR_DIV: return "div";
|
||||
case FE_IR_MOD: return "mod";
|
||||
case FE_IR_AND: return "and";
|
||||
case FE_IR_OR: return "or";
|
||||
case FE_IR_XOR: return "xor";
|
||||
case FE_IR_SHL: return "shl";
|
||||
case FE_IR_SHR: return "shr";
|
||||
case FE_IR_EQ: return "eq";
|
||||
case FE_IR_NE: return "ne";
|
||||
case FE_IR_LT: return "lt";
|
||||
case FE_IR_LE: return "le";
|
||||
case FE_IR_GT: return "gt";
|
||||
case FE_IR_GE: return "ge";
|
||||
case FE_IR_CAST: return "cast";
|
||||
case FE_IR_CALL: return "call";
|
||||
case FE_IR_COPY: return "copy";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
static const char *trap_name(FeIrTrap t)
|
||||
{
|
||||
switch (t) {
|
||||
case FE_TRAP_BOUNDS: return "bounds";
|
||||
case FE_TRAP_OVERFLOW: return "overflow";
|
||||
case FE_TRAP_DIVIDE: return "divide";
|
||||
case FE_TRAP_UNREACHABLE: return "unreachable";
|
||||
case FE_TRAP_EXPLICIT: return "trap";
|
||||
}
|
||||
return "?";
|
||||
}
|
||||
|
||||
static void dump_place(const FeIrPlace *p, FILE *out)
|
||||
{
|
||||
switch (p->base) {
|
||||
case FE_PLACE_LOCAL: fprintf(out, "$%u", p->index); break;
|
||||
case FE_PLACE_GLOBAL: fprintf(out, "@%s", p->name ? p->name : "?"); break;
|
||||
case FE_PLACE_TEMP: fprintf(out, "%%%u", p->index); break;
|
||||
}
|
||||
if (p->offset) fprintf(out, " + %ld", p->offset);
|
||||
}
|
||||
|
||||
static void dump_value(const FeIrValue *v, FILE *out)
|
||||
{
|
||||
unsigned i;
|
||||
fputs(" ", out);
|
||||
if (v->has_dest) fprintf(out, "%%%u = ", v->dest);
|
||||
switch (v->op) {
|
||||
case FE_IR_CONST:
|
||||
fprintf(out, "const %s %ld", fe_ir_type_name(v->type), v->imm);
|
||||
break;
|
||||
case FE_IR_LOAD:
|
||||
fprintf(out, "load %s ", fe_ir_type_name(v->type));
|
||||
dump_place(&v->place, out);
|
||||
break;
|
||||
case FE_IR_STORE:
|
||||
fputs("store ", out);
|
||||
dump_place(&v->place, out);
|
||||
fprintf(out, ", %%%u", v->a);
|
||||
break;
|
||||
case FE_IR_ADDR:
|
||||
fputs("addr ", out);
|
||||
dump_place(&v->place, out);
|
||||
break;
|
||||
case FE_IR_CAST:
|
||||
fprintf(out, "cast %s %s %%%u",
|
||||
fe_ir_type_name((FeIrType)v->imm),
|
||||
fe_ir_type_name(v->type), v->a);
|
||||
break;
|
||||
case FE_IR_CALL:
|
||||
fprintf(out, "call @%s(", v->callee ? v->callee : "?");
|
||||
for (i = 0; i < v->arg_count; ++i)
|
||||
fprintf(out, "%s%%%u", i ? ", " : "", v->args[i]);
|
||||
fputc(')', out);
|
||||
break;
|
||||
case FE_IR_COPY:
|
||||
fputs("copy ", out);
|
||||
dump_place(&v->place, out);
|
||||
fputs(", ", out);
|
||||
dump_place(&v->place2, out);
|
||||
fprintf(out, ", %ld", v->imm);
|
||||
break;
|
||||
default:
|
||||
fprintf(out, "%s %s %%%u, %%%u", fe_ir_op_name(v->op),
|
||||
fe_ir_type_name(v->op >= FE_IR_EQ && v->op <= FE_IR_GE ?
|
||||
(FeIrType)v->imm : v->type), v->a, v->b);
|
||||
if (v->is_unsigned) fputs(" u", out);
|
||||
break;
|
||||
}
|
||||
fputc('\n', out);
|
||||
}
|
||||
|
||||
void fe_ir_dump(const FeIrModule *m, FILE *out)
|
||||
{
|
||||
const FeIrFunc *f;
|
||||
const FeIrBlock *b;
|
||||
const FeIrValue *v;
|
||||
const FeIrGlobal *g;
|
||||
unsigned i;
|
||||
if (m->unit_file && m->unit_file[0])
|
||||
fprintf(out, "; unit file %s\n", m->unit_file);
|
||||
for (g = m->globals; g; g = g->next)
|
||||
fprintf(out, "global @%s : %s %lu\n", g->name,
|
||||
fe_ir_type_name(g->type), g->size);
|
||||
for (f = m->funcs; f; f = f->next) {
|
||||
if (f->is_extern) {
|
||||
fprintf(out, "extern fn @%s -> %s\n", f->name,
|
||||
fe_ir_type_name(f->ret));
|
||||
continue;
|
||||
}
|
||||
fprintf(out, "fn @%s -> %s%s {\n", f->name, fe_ir_type_name(f->ret),
|
||||
f->returns_by_address ? " (by address)" : "");
|
||||
for (i = 0; i < f->local_count; ++i) {
|
||||
fprintf(out, " $%u: %s", i, fe_ir_type_name(f->locals[i].type));
|
||||
if (f->locals[i].type == FE_IR_MEM)
|
||||
fprintf(out, "<%lu>", f->locals[i].size);
|
||||
if (i < f->param_count) fputs(" ; parameter", out);
|
||||
if (f->locals[i].name) fprintf(out, " ; %s", f->locals[i].name);
|
||||
fputc('\n', out);
|
||||
}
|
||||
for (b = f->first; b; b = b->next) {
|
||||
fprintf(out, " b%u:\n", b->id);
|
||||
for (v = b->first; v; v = v->next) dump_value(v, out);
|
||||
switch (b->term) {
|
||||
case FE_IR_JMP:
|
||||
fprintf(out, " jmp b%u\n", b->target); break;
|
||||
case FE_IR_BR:
|
||||
fprintf(out, " br %%%u, b%u, b%u\n", b->cond, b->target,
|
||||
b->target_else); break;
|
||||
case FE_IR_RET:
|
||||
if (b->has_ret_value) fprintf(out, " ret %%%u\n", b->ret_value);
|
||||
else fputs(" ret\n", out);
|
||||
break;
|
||||
case FE_IR_TRAP:
|
||||
fprintf(out, " trap %s %lu\n", trap_name(b->trap),
|
||||
b->trap_line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
fputs("}\n", out);
|
||||
}
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
#ifndef FE_IR_H
|
||||
#define FE_IR_H
|
||||
|
||||
#include "arena.h"
|
||||
#include <stdio.h>
|
||||
|
||||
/* The intermediate representation. `IR.md` is the description; this is the
|
||||
shape it takes in memory.
|
||||
|
||||
Ferro types do not survive into here. A struct, a slice, an optional and an
|
||||
error union are all `mem<N>`, and a field is a byte offset that lowering
|
||||
worked out. The machine types are what a register can hold plus a size. */
|
||||
|
||||
typedef enum FeIrType {
|
||||
FE_IR_VOID,
|
||||
FE_IR_I8, FE_IR_I16, FE_IR_I32,
|
||||
FE_IR_PTR,
|
||||
FE_IR_MEM /* size lives on the value or slot */
|
||||
} FeIrType;
|
||||
|
||||
typedef enum FeIrOp {
|
||||
FE_IR_CONST, FE_IR_LOAD, FE_IR_STORE, FE_IR_ADDR,
|
||||
FE_IR_ADD, FE_IR_SUB, FE_IR_MUL, FE_IR_DIV, FE_IR_MOD,
|
||||
FE_IR_AND, FE_IR_OR, FE_IR_XOR, FE_IR_SHL, FE_IR_SHR,
|
||||
FE_IR_EQ, FE_IR_NE, FE_IR_LT, FE_IR_LE, FE_IR_GT, FE_IR_GE,
|
||||
FE_IR_CAST, FE_IR_CALL, FE_IR_COPY
|
||||
} FeIrOp;
|
||||
|
||||
typedef enum FeIrTerm {
|
||||
FE_IR_JMP, FE_IR_BR, FE_IR_RET, FE_IR_TRAP
|
||||
} FeIrTerm;
|
||||
|
||||
/* Why a program stopped. Kept small and stable: it is a number in the
|
||||
executable, and the runtime turns it back into words. */
|
||||
typedef enum FeIrTrap {
|
||||
FE_TRAP_BOUNDS = 0,
|
||||
FE_TRAP_OVERFLOW = 1,
|
||||
FE_TRAP_DIVIDE = 2,
|
||||
FE_TRAP_UNREACHABLE = 3,
|
||||
FE_TRAP_EXPLICIT = 4
|
||||
} FeIrTrap;
|
||||
|
||||
/* Where an instruction reads or writes. A place is a base plus a constant
|
||||
offset; anything computed goes through a pointer temporary instead. */
|
||||
typedef enum FeIrBase {
|
||||
FE_PLACE_LOCAL, /* $n */
|
||||
FE_PLACE_GLOBAL, /* @name */
|
||||
FE_PLACE_TEMP /* %p */
|
||||
} FeIrBase;
|
||||
|
||||
typedef struct FeIrPlace {
|
||||
FeIrBase base;
|
||||
unsigned index; /* local or temp number */
|
||||
const char *name; /* global name */
|
||||
long offset;
|
||||
} FeIrPlace;
|
||||
|
||||
typedef struct FeIrValue {
|
||||
FeIrOp op;
|
||||
FeIrType type;
|
||||
unsigned dest; /* %dest, or 0 when the op has no result */
|
||||
int has_dest;
|
||||
/* operands, by role -- only the ones the op uses are set */
|
||||
unsigned a, b; /* temporaries */
|
||||
long imm; /* const, cast width, copy size */
|
||||
FeIrPlace place; /* load / store / addr / copy destination */
|
||||
FeIrPlace place2; /* copy source */
|
||||
const char *callee;
|
||||
unsigned *args;
|
||||
unsigned arg_count;
|
||||
int is_unsigned; /* picks the signed or unsigned instruction */
|
||||
unsigned long line; /* for diagnostics that survive into the backend */
|
||||
struct FeIrValue *next;
|
||||
} FeIrValue;
|
||||
|
||||
typedef struct FeIrFunc FeIrFunc;
|
||||
|
||||
typedef struct FeIrBlock {
|
||||
unsigned id;
|
||||
FeIrFunc *func; /* the function this block is being built in */
|
||||
FeIrValue *first;
|
||||
FeIrValue *last;
|
||||
FeIrTerm term;
|
||||
unsigned cond; /* br */
|
||||
unsigned target; /* jmp, br true */
|
||||
unsigned target_else; /* br false */
|
||||
unsigned ret_value; /* ret */
|
||||
int has_ret_value;
|
||||
FeIrTrap trap;
|
||||
unsigned long trap_line;
|
||||
struct FeIrBlock *next;
|
||||
} FeIrBlock;
|
||||
|
||||
typedef struct FeIrLocal {
|
||||
FeIrType type;
|
||||
unsigned long size; /* for FE_IR_MEM */
|
||||
unsigned align;
|
||||
const char *name; /* the Ferro name, for reading the dump */
|
||||
} FeIrLocal;
|
||||
|
||||
struct FeIrFunc {
|
||||
const char *name; /* unit.name */
|
||||
FeIrType ret;
|
||||
unsigned long ret_size; /* when ret is FE_IR_MEM */
|
||||
/* A function returning mem<N> takes the address to write as a hidden
|
||||
first parameter, so the caller owns the storage. */
|
||||
int returns_by_address;
|
||||
FeIrLocal *locals;
|
||||
unsigned local_count;
|
||||
unsigned local_capacity;
|
||||
unsigned param_count; /* the first `param_count` locals are parameters */
|
||||
unsigned temp_count;
|
||||
FeIrBlock *first;
|
||||
FeIrBlock *last;
|
||||
unsigned block_count;
|
||||
int is_extern;
|
||||
struct FeIrFunc *next;
|
||||
};
|
||||
|
||||
typedef struct FeIrGlobal {
|
||||
const char *name;
|
||||
FeIrType type;
|
||||
unsigned long size;
|
||||
unsigned align;
|
||||
const unsigned char *init; /* size bytes, or null for zero */
|
||||
struct FeIrGlobal *next;
|
||||
} FeIrGlobal;
|
||||
|
||||
typedef struct FeIrModule {
|
||||
FeArena arena;
|
||||
const char *unit_file; /* the one file-name string a unit's traps share */
|
||||
FeIrFunc *funcs;
|
||||
FeIrFunc *last_func;
|
||||
FeIrGlobal *globals;
|
||||
FeIrGlobal *last_global;
|
||||
} FeIrModule;
|
||||
|
||||
void fe_ir_module_init(FeIrModule *m);
|
||||
void fe_ir_module_destroy(FeIrModule *m);
|
||||
|
||||
FeIrFunc *fe_ir_func(FeIrModule *m, const char *name, FeIrType ret,
|
||||
unsigned long ret_size);
|
||||
unsigned fe_ir_local(FeIrModule *m, FeIrFunc *f, FeIrType type,
|
||||
unsigned long size, unsigned align, const char *name);
|
||||
unsigned fe_ir_temp(FeIrFunc *f);
|
||||
FeIrBlock *fe_ir_block(FeIrModule *m, FeIrFunc *f);
|
||||
|
||||
/* Places */
|
||||
FeIrPlace fe_ir_at_local(unsigned index, long offset);
|
||||
FeIrPlace fe_ir_at_global(const char *name, long offset);
|
||||
FeIrPlace fe_ir_at_temp(unsigned temp, long offset);
|
||||
|
||||
/* Instructions. Each returns the destination temporary where there is one. */
|
||||
unsigned fe_ir_const(FeIrModule *m, FeIrBlock *b, FeIrType t, long v);
|
||||
unsigned fe_ir_load(FeIrModule *m, FeIrBlock *b, FeIrType t, FeIrPlace p);
|
||||
void fe_ir_store(FeIrModule *m, FeIrBlock *b, FeIrPlace p, unsigned v);
|
||||
unsigned fe_ir_addr(FeIrModule *m, FeIrBlock *b, FeIrPlace p);
|
||||
unsigned fe_ir_binary(FeIrModule *m, FeIrBlock *b, FeIrOp op, FeIrType t,
|
||||
unsigned a, unsigned c, int is_unsigned);
|
||||
unsigned fe_ir_cast(FeIrModule *m, FeIrBlock *b, FeIrType from, FeIrType to,
|
||||
unsigned a, int is_unsigned);
|
||||
unsigned fe_ir_call(FeIrModule *m, FeIrBlock *b, FeIrType ret,
|
||||
const char *callee, unsigned *args, unsigned count);
|
||||
void fe_ir_copy(FeIrModule *m, FeIrBlock *b, FeIrPlace dst, FeIrPlace src,
|
||||
unsigned long size);
|
||||
|
||||
/* Terminators */
|
||||
void fe_ir_jmp(FeIrBlock *b, unsigned target);
|
||||
void fe_ir_br(FeIrBlock *b, unsigned cond, unsigned t, unsigned f);
|
||||
void fe_ir_ret(FeIrBlock *b, unsigned value, int has_value);
|
||||
void fe_ir_trap(FeIrBlock *b, FeIrTrap reason, unsigned long line);
|
||||
|
||||
void fe_ir_dump(const FeIrModule *m, FILE *out);
|
||||
const char *fe_ir_type_name(FeIrType t);
|
||||
const char *fe_ir_op_name(FeIrOp op);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user