Merging master to handle vector members, fields, and members of vector fields

This commit is contained in:
Wolfgang (Blub) Bumiller 2012-08-12 10:08:41 +02:00
commit d68d19dbfd
9 changed files with 401 additions and 37 deletions

44
ast.c
View file

@ -94,6 +94,15 @@ static ast_value* ast_value_copy(const ast_value *self)
return cp;
}
static ast_expression* ast_shallow_type(lex_ctx ctx, int vtype)
{
ast_instantiate(ast_expression, ctx, ast_expression_delete_full);
self->expression.codegen = NULL;
self->expression.next = NULL;
self->expression.vtype = vtype;
return self;
}
static ast_expression* ast_type_copy(lex_ctx ctx, const ast_expression *ex)
{
size_t i;
@ -306,10 +315,22 @@ ast_member* ast_member_new(lex_ctx ctx, ast_expression *owner, unsigned int fiel
return NULL;
}
if (owner->expression.vtype != TYPE_FLOAT &&
owner->expression.vtype != TYPE_FIELD) {
printf("ast_member on an invalid owner of type %i\n", (int)owner->expression.vtype);
mem_d(self);
return NULL;
}
ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_member_codegen);
self->expression.vtype = TYPE_FLOAT;
self->expression.next = NULL;
if (owner->expression.vtype == TYPE_VECTOR) {
self->expression.vtype = TYPE_FLOAT;
self->expression.next = NULL;
} else {
self->expression.vtype = TYPE_FIELD;
self->expression.next = ast_shallow_type(ctx, TYPE_FLOAT);
}
self->owner = owner;
self->field = field;
@ -628,6 +649,18 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir)
return true;
}
if (self->expression.vtype == TYPE_FIELD) {
v = ir_builder_create_field(ir, self->name, self->expression.next->expression.vtype);
if (!v)
return false;
if (self->isconst) {
printf("TODO: constant field pointers with value\n");
goto error;
}
self->ir_v = v;
return true;
}
v = ir_builder_create_global(ir, self->name, self->expression.vtype);
if (!v) {
printf("ir_builder_create_global failed\n");
@ -954,14 +987,17 @@ bool ast_entfield_codegen(ast_entfield *self, ast_function *func, bool lvalue, i
bool ast_member_codegen(ast_member *self, ast_function *func, bool lvalue, ir_value **out)
{
ast_expression_codegen *cgen;
ir_value *vec, *field;
ir_value *vec;
cgen = self->owner->expression.codegen;
if (!(*cgen)((ast_expression*)(self->owner), func, true, &vec))
return false;
if (vec->vtype != TYPE_VECTOR)
if (vec->vtype != TYPE_VECTOR &&
!(vec->vtype == TYPE_FIELD && self->owner->expression.next->expression.vtype == TYPE_VECTOR))
{
return false;
}
*out = ir_value_vector_member(vec, self->field);

140
exec.c
View file

@ -13,6 +13,7 @@ MEM_VEC_FUN_APPEND(qc_program, char, strings)
MEM_VEC_FUN_RESIZE(qc_program, char, strings)
MEM_VEC_FUNCTIONS(qc_program, qcint, globals)
MEM_VEC_FUNCTIONS(qc_program, qcint, entitydata)
MEM_VEC_FUNCTIONS(qc_program, bool, entitypool)
MEM_VEC_FUNCTIONS(qc_program, qcint, localstack)
MEM_VEC_FUN_APPEND(qc_program, qcint, localstack)
@ -34,10 +35,20 @@ static void loaderror(const char *fmt, ...)
printf(": %s\n", strerror(err));
}
static void printvmerr(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
va_end(ap);
putchar('\n');
}
qc_program* prog_load(const char *filename)
{
qc_program *prog;
prog_header header;
size_t i;
FILE *file;
file = fopen(filename, "rb");
@ -108,6 +119,19 @@ qc_program* prog_load(const char *filename)
if (!qc_program_strings_resize(prog, prog->strings_count + 16*1024))
goto error;
/* spawn the world entity */
if (!qc_program_entitypool_add(prog, true)) {
loaderror("failed to allocate world entity\n");
goto error;
}
for (i = 0; i < prog->entityfields; ++i) {
if (!qc_program_entitydata_add(prog, 0)) {
loaderror("failed to allocate world data\n");
goto error;
}
}
prog->entities = 1;
return prog;
error:
@ -119,6 +143,7 @@ error:
if (prog->strings) mem_d(prog->strings);
if (prog->globals) mem_d(prog->globals);
if (prog->entitydata) mem_d(prog->entitydata);
if (prog->entitypool) mem_d(prog->entitypool);
mem_d(prog);
return NULL;
}
@ -133,6 +158,7 @@ void prog_delete(qc_program *prog)
MEM_VECTOR_CLEAR(prog, strings);
MEM_VECTOR_CLEAR(prog, globals);
MEM_VECTOR_CLEAR(prog, entitydata);
MEM_VECTOR_CLEAR(prog, entitypool);
MEM_VECTOR_CLEAR(prog, localstack);
MEM_VECTOR_CLEAR(prog, stack);
MEM_VECTOR_CLEAR(prog, profile);
@ -177,7 +203,58 @@ prog_section_def* prog_getdef(qc_program *prog, qcint off)
qcany* prog_getedict(qc_program *prog, qcint e)
{
return (qcany*)(prog->entitydata + (prog->entityfields + e));
if (e >= prog->entitypool_count) {
prog->vmerror++;
printf("Accessing out of bounds edict %i\n", (int)e);
e = 0;
}
return (qcany*)(prog->entitydata + (prog->entityfields * e));
}
qcint prog_spawn_entity(qc_program *prog)
{
size_t i;
qcint e;
for (e = 0; e < (qcint)prog->entitypool_count; ++e) {
if (!prog->entitypool[e]) {
char *data = (char*)(prog->entitydata + (prog->entityfields * e));
memset(data, 0, prog->entityfields * sizeof(qcint));
return e;
}
}
if (!qc_program_entitypool_add(prog, true)) {
prog->vmerror++;
printf("Failed to allocate entity\n");
return 0;
}
prog->entities++;
for (i = 0; i < prog->entityfields; ++i) {
if (!qc_program_entitydata_add(prog, 0)) {
printf("Failed to allocate entity\n");
return 0;
}
}
return e;
}
void prog_free_entity(qc_program *prog, qcint e)
{
if (!e) {
prog->vmerror++;
printf("Trying to free world entity\n");
return;
}
if (e >= prog->entitypool_count) {
prog->vmerror++;
printf("Trying to free out of bounds entity\n");
return;
}
if (!prog->entitypool[e]) {
prog->vmerror++;
printf("Double free on entity\n");
return;
}
prog->entitypool[e] = false;
}
qcint prog_tempstring(qc_program *prog, const char *_str)
@ -439,6 +516,8 @@ bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long
long jumpcount = 0;
prog_section_statement *st;
prog->vmerror = 0;
st = prog->code + prog_enterfunction(prog, func);
--st;
switch (flags)
@ -477,6 +556,8 @@ bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long
cleanup:
prog->localstack_count = 0;
prog->stack_count = 0;
if (prog->vmerror)
return false;
return true;
}
@ -488,16 +569,66 @@ cleanup:
bool opts_debug = false;
bool opts_memchk = false;
#define CheckArgs(num) do { \
if (prog->argc != (num)) { \
prog->vmerror++; \
printf("ERROR: invalid number of arguments for %s: %i, expected %i\n", \
__FUNCTION__, prog->argc, (num)); \
return -1; \
} \
} while (0)
#define GetGlobal(idx) ((qcany*)(prog->globals + (idx)))
#define GetArg(num) GetGlobal(OFS_PARM0 + 3*(num))
#define Return(any) *(GetGlobal(OFS_RETURN)) = (any)
static int qc_print(qc_program *prog)
{
qcany *str = (qcany*)(prog->globals + OFS_PARM0);
printf("%s", prog_getstring(prog, str->string));
size_t i;
for (i = 0; i < prog->argc; ++i) {
qcany *str = (qcany*)(prog->globals + OFS_PARM0 + 3*i);
printf("%s", prog_getstring(prog, str->string));
}
return 0;
}
static int qc_ftos(qc_program *prog)
{
char buffer[512];
qcany *num;
qcany str;
CheckArgs(1);
num = GetArg(0);
snprintf(buffer, sizeof(buffer), "%g", num->_float);
str.string = prog_tempstring(prog, buffer);
Return(str);
return 0;
}
static int qc_spawn(qc_program *prog)
{
qcany ent;
CheckArgs(0);
ent.edict = prog_spawn_entity(prog);
Return(ent);
return (ent.edict ? 0 : -1);
}
static int qc_kill(qc_program *prog)
{
qcany *ent;
CheckArgs(1);
ent = GetArg(0);
prog_free_entity(prog, ent->edict);
return 0;
}
static prog_builtin qc_builtins[] = {
NULL,
&qc_print
&qc_print,
&qc_ftos,
&qc_spawn,
&qc_kill
};
static size_t qc_builtins_count = sizeof(qc_builtins) / sizeof(qc_builtins[0]);
@ -528,6 +659,7 @@ int main(int argc, char **argv)
if (!strcmp(name, "main"))
fnmain = (qcint)i;
}
printf("Entity field space: %i\n", (int)prog->entityfields);
if (fnmain > 0)
{
prog_exec(prog, &prog->functions[fnmain], VMXF_TRACE, VM_JUMPS_DEFAULT);

View file

@ -16,7 +16,7 @@
#endif
#if !defined(PRVM_ERROR)
# define PRVM_ERROR printf
# define PRVM_ERROR prog->vmerror++, printvmerr
#endif
#if !defined(PRVM_NAME)
@ -216,7 +216,7 @@
ptr->_int = OPA->_int;
break;
case INSTR_STOREP_V:
if (OPB->_int < 0 || OPB->_int + 3 >= prog->entitydata_count)
if (OPB->_int < 0 || OPB->_int + 2 >= prog->entitydata_count)
{
PRVM_ERROR("%s attempted to write to an out of bounds edict (%i)", PRVM_NAME, OPB->_int);
goto cleanup;
@ -232,7 +232,7 @@
case INSTR_ADDRESS:
if (OPA->edict < 0 || OPA->edict >= prog->entities)
{
PRVM_ERROR ("%s Progs attempted to address an out of bounds edict number", PRVM_NAME);
PRVM_ERROR ("%s Progs attempted to address an out of bounds edict number %i", PRVM_NAME, OPA->edict);
goto cleanup;
}
if ((unsigned int)(OPB->_int) >= (unsigned int)(prog->entityfields))
@ -271,7 +271,7 @@
PRVM_ERROR ("%s Progs attempted to read an out of bounds edict number", PRVM_NAME);
goto cleanup;
}
if (OPB->_int < 0 || OPB->_int + 3 >= prog->entityfields)
if (OPB->_int < 0 || OPB->_int + 3 > prog->entityfields)
{
PRVM_ERROR("%s attempted to read an invalid field in an edict (%i)", PRVM_NAME, OPB->_int);
goto cleanup;
@ -357,6 +357,8 @@
}
else
st = prog->code + PROG_ENTERFUNCTION(newf);
if (prog->vmerror)
goto cleanup;
break;
case INSTR_DONE:

View file

@ -5,3 +5,4 @@
GMQCC_DEFINE_FLAG(OVERLAP_LOCALS)
GMQCC_DEFINE_FLAG(DARKPLACES_STRING_TABLE_BUG)
GMQCC_DEFINE_FLAG(OMIT_NULL_BYTES)
GMQCC_DEFINE_FLAG(ADJUST_VECTOR_FIELDS)

View file

@ -885,6 +885,7 @@ typedef struct qc_program_s {
MEM_VECTOR_MAKE(char, strings);
MEM_VECTOR_MAKE(qcint, globals);
MEM_VECTOR_MAKE(qcint, entitydata);
MEM_VECTOR_MAKE(bool, entitypool);
size_t tempstring_start;
size_t tempstring_at;

138
ir.c
View file

@ -559,6 +559,8 @@ void ir_value_code_setaddr(ir_value *self, int32_t gaddr)
int32_t ir_value_code_addr(const ir_value *self)
{
if (self->store == store_return)
return OFS_RETURN + self->code.addroffset;
return self->code.globaladdr + self->code.addroffset;
}
@ -594,13 +596,29 @@ ir_value* ir_value_vector_member(ir_value *self, unsigned int member)
if (self->members[member])
return self->members[member];
m = ir_value_var(self->name, self->store, TYPE_FLOAT);
if (!m)
return NULL;
m->context = self->context;
if (self->vtype == TYPE_VECTOR)
{
m = ir_value_var(self->name, self->store, TYPE_FLOAT);
if (!m)
return NULL;
m->context = self->context;
self->members[member] = m;
m->code.addroffset = member;
self->members[member] = m;
m->code.addroffset = member;
}
else if (self->vtype == TYPE_FIELD)
{
if (self->fieldtype != TYPE_VECTOR)
return NULL;
m = ir_value_var(self->name, self->store, TYPE_FIELD);
if (!m)
return NULL;
m->fieldtype = TYPE_FLOAT;
m->context = self->context;
self->members[member] = m;
m->code.addroffset = member;
}
return m;
}
@ -694,6 +712,15 @@ bool ir_value_set_matrix(ir_value *self, matrix v)
return true;
}
bool ir_value_set_field(ir_value *self, ir_value *fld)
{
if (self->vtype != TYPE_FIELD)
return false;
self->constval.vpointer = fld;
self->isconst = true;
return true;
}
bool ir_value_set_string(ir_value *self, const char *str)
{
if (self->vtype != TYPE_STRING)
@ -945,22 +972,26 @@ bool ir_values_overlap(const ir_value *a, const ir_value *b)
bool ir_block_create_store_op(ir_block *self, int op, ir_value *target, ir_value *what)
{
if (target->store == store_value) {
ir_instr *in = ir_instr_new(self, op);
if (!in)
return false;
if (target->store == store_value &&
(op < INSTR_STOREP_F || op > INSTR_STOREP_FNC))
{
fprintf(stderr, "cannot store to an SSA value\n");
fprintf(stderr, "trying to store: %s <- %s\n", target->name, what->name);
fprintf(stderr, "instruction: %s\n", asm_instr[op].m);
return false;
} else {
ir_instr *in = ir_instr_new(self, op);
if (!in)
return false;
if (!ir_instr_op(in, 0, target, true) ||
!ir_instr_op(in, 1, what, false) ||
!ir_block_instr_add(self, in) )
{
return false;
}
return true;
}
if (!ir_instr_op(in, 0, target, true) ||
!ir_instr_op(in, 1, what, false) ||
!ir_block_instr_add(self, in) )
{
return false;
}
return true;
}
bool ir_block_create_store(ir_block *self, ir_value *target, ir_value *what)
@ -980,6 +1011,11 @@ bool ir_block_create_store(ir_block *self, ir_value *target, ir_value *what)
#endif
op = type_store_instr[vtype];
if (OPTS_FLAG(ADJUST_VECTOR_FIELDS)) {
if (op == INSTR_STORE_FLD && what->fieldtype == TYPE_VECTOR)
op = INSTR_STORE_V;
}
return ir_block_create_store_op(self, op, target, what);
}
@ -997,6 +1033,11 @@ bool ir_block_create_storep(ir_block *self, ir_value *target, ir_value *what)
vtype = what->vtype;
op = type_storep_instr[vtype];
if (OPTS_FLAG(ADJUST_VECTOR_FIELDS)) {
if (op == INSTR_STOREP_FLD && what->fieldtype == TYPE_VECTOR)
op = INSTR_STOREP_V;
}
return ir_block_create_store_op(self, op, target, what);
}
@ -2178,10 +2219,18 @@ static bool gen_global_field(ir_value *global)
/* copy the field's value */
ir_value_code_setaddr(global, code_globals_add(code_globals_data[fld->code.globaladdr]));
if (global->fieldtype == TYPE_VECTOR) {
code_globals_add(code_globals_data[fld->code.globaladdr]+1);
code_globals_add(code_globals_data[fld->code.globaladdr]+2);
}
}
else
{
ir_value_code_setaddr(global, code_globals_add(0));
if (global->fieldtype == TYPE_VECTOR) {
code_globals_add(0);
code_globals_add(0);
}
}
if (global->code.globaladdr < 0)
return false;
@ -2408,8 +2457,10 @@ tailcall:
stmt.o1.u1 = stmt.o3.u1;
stmt.o3.u1 = 0;
}
else if (stmt.opcode >= INSTR_STORE_F &&
stmt.opcode <= INSTR_STORE_FNC)
else if ((stmt.opcode >= INSTR_STORE_F &&
stmt.opcode <= INSTR_STORE_FNC) ||
(stmt.opcode >= INSTR_STOREP_F &&
stmt.opcode <= INSTR_STOREP_FNC))
{
/* 2-operand instructions with A -> B */
stmt.o2.u1 = stmt.o3.u1;
@ -2622,13 +2673,42 @@ static bool ir_builder_gen_field(ir_builder *self, ir_value *field)
def.type = field->vtype;
def.offset = code_globals_elements;
def.name = field->code.name = code_genstring(field->name);
/* create a global named the same as the field */
if (opts_standard == COMPILER_GMQCC) {
/* in our standard, the global gets a dot prefix */
size_t len = strlen(field->name);
char name[1024];
/* we really don't want to have to allocate this, and 1024
* bytes is more than enough for a variable/field name
*/
if (len+2 >= sizeof(name)) {
printf("invalid field name size: %u\n", (unsigned int)len);
return false;
}
name[0] = '.';
strcpy(name+1, field->name); /* no strncpy - we used strlen above */
name[len+1] = 0;
def.name = code_genstring(name);
fld.name = def.name + 1; /* we reuse that string table entry */
} else {
/* in plain QC, there cannot be a global with the same name,
* and so we also name the global the same.
* FIXME: fteqcc should create a global as well
* check if it actually uses the same name. Probably does
*/
def.name = code_genstring(field->name);
fld.name = def.name;
}
field->code.name = def.name;
if (code_defs_add(def) < 0)
return false;
fld.name = def.name;
fld.offset = code_fields_elements;
fld.type = field->fieldtype;
if (fld.type == TYPE_VOID) {
@ -2636,13 +2716,21 @@ static bool ir_builder_gen_field(ir_builder *self, ir_value *field)
return false;
}
fld.offset = code_alloc_field(type_sizeof[field->fieldtype]);
if (code_fields_add(fld) < 0)
return false;
if (!code_globals_add(code_alloc_field(type_sizeof[field->fieldtype])))
ir_value_code_setaddr(field, code_globals_elements);
if (!code_globals_add(fld.offset))
return false;
if (fld.type == TYPE_VECTOR) {
if (!code_globals_add(fld.offset+1))
return false;
if (!code_globals_add(fld.offset+2))
return false;
}
ir_value_code_setaddr(field, code_globals_add(fld.offset));
return field->code.globaladdr >= 0;
}

1
ir.h
View file

@ -97,6 +97,7 @@ bool GMQCC_WARN ir_value_set_int(ir_value*, int i);
#endif
bool GMQCC_WARN ir_value_set_string(ir_value*, const char *s);
bool GMQCC_WARN ir_value_set_vector(ir_value*, vector v);
bool GMQCC_WARN ir_value_set_field(ir_value*, ir_value *fld);
/*bool ir_value_set_pointer_v(ir_value*, ir_value* p); */
/*bool ir_value_set_pointer_i(ir_value*, int i); */
bool GMQCC_WARN ir_value_set_quaternion(ir_value*, quaternion v);

View file

@ -21,6 +21,17 @@ name = ast_value_new(ctx, #varname, type)
#define MKGLOBAL(name) \
assert(globals_add(name) >= 0)
#define FIELD(type, name) \
name = ast_value_new(ctx, #name, TYPE_FIELD); \
do { \
ast_value *field_##name = ast_value_new(ctx, #name, type); \
name->expression.next = (ast_expression*)field_##name; \
MKFIELD(name); \
} while (0)
#define MKFIELD(name) \
assert(fields_add(name) >= 0)
#define MKCONSTFLOAT(name, value) \
do { \
name->isconst = true; \
@ -35,6 +46,15 @@ do { \
MKGLOBAL(name); \
} while(0)
#define MKCONSTVECTOR(name, valx, valy, valz) \
do { \
name->isconst = true; \
name->constval.vvec.x = (valx); \
name->constval.vvec.y = (valy); \
name->constval.vvec.z = (valz); \
MKGLOBAL(name); \
} while(0)
#define STATE(a) \
do { \
ast_expression *exp = (ast_expression*)(a); \
@ -47,6 +67,12 @@ do { \
#define BIN(op, a, b) \
(ast_expression*)ast_binary_new(ctx, INSTR_##op, (ast_expression*)(a), (ast_expression*)(b))
#define ENTFIELD(a, b) \
(ast_expression*)ast_entfield_new(ctx, (ast_expression*)(a), (ast_expression*)(b))
#define VECMEM(vec, mem) \
(ast_expression*)ast_member_new(ctx, (ast_expression*)(vec), (mem))
#define CALL(what) \
do { \
ast_call *call = ast_call_new(ctx, (ast_expression*)what); \
@ -58,6 +84,13 @@ do { \
STATE(call); \
} while(0)
#define ENDCALLWITH(as, where) \
{ \
ast_expression *as = (ast_expression*)call; \
where; \
} \
} while(0)
#define WHILE(cond) \
do { \
ast_expression *wh_cond = (ast_expression*)(cond); \

View file

@ -10,6 +10,7 @@
#define assert(x) do { if ( !(x) ) { printf("Assertion failed: %s\n", #x); abort(); } } while(0)
VECTOR_MAKE(ast_value*, globals);
VECTOR_MAKE(ast_value*, fields);
VECTOR_MAKE(ast_function*, functions);
uint32_t opts_flags[1 + (COUNT_FLAGS / 32)];
@ -36,13 +37,30 @@ int main()
DEFVAR(f0);
DEFVAR(f1);
DEFVAR(f5);
DEFVAR(cv3x4x5);
DEFVAR(cv1x1x1);
DEFVAR(sHello);
DEFVAR(sNL);
DEFVAR(print);
DEFVAR(ftos);
DEFVAR(spawn);
DEFVAR(mema);
DEFVAR(memb);
DEFVAR(memv);
DEFVAR(pawn);
/* opts_debug = true; */
BUILTIN(print, TYPE_VOID, -1);
PARAM(TYPE_STRING, text);
ENDBUILTIN();
BUILTIN(ftos, TYPE_STRING, -2);
PARAM(TYPE_FLOAT, value);
ENDBUILTIN();
BUILTIN(spawn, TYPE_ENTITY, -3);
ENDBUILTIN();
TESTINIT();
@ -50,21 +68,36 @@ VAR(TYPE_FLOAT, f0);
VAR(TYPE_FLOAT, f1);
VAR(TYPE_FLOAT, f5);
VAR(TYPE_STRING, sHello);
VAR(TYPE_STRING, sNL);
VAR(TYPE_VECTOR, cv3x4x5);
VAR(TYPE_VECTOR, cv1x1x1);
FIELD(TYPE_FLOAT, mema);
FIELD(TYPE_FLOAT, memb);
FIELD(TYPE_VECTOR, memv);
MKCONSTFLOAT(f0, 0.0);
MKCONSTFLOAT(f1, 1.0);
MKCONSTFLOAT(f5, 5.0);
MKCONSTSTRING(sHello, "Hello, World\n");
MKCONSTSTRING(sNL, "\n");
MKCONSTVECTOR(cv3x4x5, 3, 4, 5);
MKCONSTVECTOR(cv1x1x1, 1, 1, 1);
FUNCTION(foo, TYPE_VOID);
ENDFUNCTION(foo);
#define PRINTNL() do { CALL(print) CALLPARAM(sNL) ENDCALL(); } while(0)
FUNCTION(main, TYPE_VOID);
VAR(TYPE_FLOAT, vi);
VAR(TYPE_FLOAT, vx);
VAR(TYPE_ENTITY, pawn);
MKLOCAL(vi);
MKLOCAL(vx);
MKLOCAL(pawn);
STATE(ASSIGN(STORE_F, vi, f0));
WHILE(BIN(LT, vi, f5));
@ -76,11 +109,48 @@ FUNCTION(main, TYPE_VOID);
CALLPARAM(sHello)
ENDCALL();
CALL(spawn)
ENDCALLWITH(newent, STATE(ASSIGN(STORE_ENT, pawn, newent)));
STATE(ASSIGN(STOREP_F, ENTFIELD(pawn, mema), f5));
STATE(ASSIGN(STOREP_F, ENTFIELD(pawn, memb), f1));
STATE(ASSIGN(STOREP_V, ENTFIELD(pawn, memv), cv3x4x5));
CALL(ftos)
CALLPARAM(ENTFIELD(pawn, mema))
ENDCALLWITH(output,
CALL(print)
CALLPARAM(output)
CALLPARAM(sNL)
ENDCALL();
);
CALL(ftos)
CALLPARAM(ENTFIELD(pawn, memb))
ENDCALLWITH(output,
CALL(print)
CALLPARAM(output)
CALLPARAM(sNL)
ENDCALL();
);
CALL(ftos)
CALLPARAM(ENTFIELD(pawn, VECMEM(memv, 2)))
ENDCALLWITH(output,
CALL(print)
CALLPARAM(output)
CALLPARAM(sNL)
ENDCALL();
);
ENDFUNCTION(main);
ir = ir_builder_new("ast_test");
assert(ir);
/* gen fields */
for (i = 0; i < fields_elements; ++i) {
if (!ast_global_codegen(fields_data[i], ir)) {
assert(!"failed to generate field");
}
}
/* gen globals */
for (i = 0; i < globals_elements; ++i) {
if (!ast_global_codegen(globals_data[i], ir)) {