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;
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;
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");
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);
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)
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");
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:
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;
}
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);
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)
long jumpcount = 0;
prog_section_statement *st;
+ prog->vmerror = 0;
+
st = prog->code + prog_enterfunction(prog, func);
--st;
switch (flags)
cleanup:
prog->localstack_count = 0;
prog->stack_count = 0;
+ if (prog->vmerror)
+ return false;
return true;
}
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]);
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);
#endif
#if !defined(PRVM_ERROR)
-# define PRVM_ERROR printf
+# define PRVM_ERROR prog->vmerror++, printvmerr
#endif
#if !defined(PRVM_NAME)
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;
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))
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;
}
else
st = prog->code + PROG_ENTERFUNCTION(newf);
+ if (prog->vmerror)
+ goto cleanup;
break;
case INSTR_DONE:
GMQCC_DEFINE_FLAG(OVERLAP_LOCALS)
GMQCC_DEFINE_FLAG(DARKPLACES_STRING_TABLE_BUG)
GMQCC_DEFINE_FLAG(OMIT_NULL_BYTES)
+GMQCC_DEFINE_FLAG(ADJUST_VECTOR_FIELDS)
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;
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;
}
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;
}
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)
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;
+ }
+
+ if (!ir_instr_op(in, 0, target, true) ||
+ !ir_instr_op(in, 1, what, false) ||
+ !ir_block_instr_add(self, in) )
+ {
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;
}
+ return true;
}
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);
}
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);
}
/* 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;
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;
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) {
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;
}
#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);
#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; \
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); \
#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); \
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); \
#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)];
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();
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));
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)) {