C-style c_varargs (Not heavily tested)

This commit is contained in:
Ginger Bill
2017-06-13 21:00:42 +01:00
parent e7fc24e48c
commit c3b510c2d9
7 changed files with 202 additions and 55 deletions
+6 -3
View File
@@ -293,13 +293,16 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
}
if (is_foreign && is_export) {
error_node(pd->type, "You cannot apply both `foreign` and `export` to a procedure");
error_node(pd->type, "A foreign procedure cannot have an `export` tag");
}
if (pd->body != NULL) {
if (is_foreign) {
error_node(pd->body, "A procedure tagged as `foreign` cannot have a body");
error_node(pd->body, "A foreign procedure cannot have a body");
}
if (proc_type->Proc.c_vararg) {
error_node(pd->body, "A procedure with a `#c_vararg` field cannot have a body");
}
d->scope = c->context.scope;
@@ -360,7 +363,7 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
Type *other_type = base_type(f->type);
if (!are_signatures_similar_enough(this_type, other_type)) {
error_node(d->proc_decl,
"Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
"Redeclaration of foreign procedure `%.*s` with different type signatures\n"
"\tat %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column);
}
+39 -1
View File
@@ -1049,6 +1049,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
}
bool is_variadic = false;
bool is_c_vararg = false;
Entity **variables = gb_alloc_array(c->allocator, Entity *, variable_count);
isize variable_index = 0;
for_array(i, params) {
@@ -1114,10 +1115,19 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
if (p->flags&FieldFlag_no_alias) {
if (!is_type_pointer(type)) {
error_node(params[i], "`no_alias` can only be applied to fields of pointer type");
error_node(params[i], "`#no_alias` can only be applied to fields of pointer type");
p->flags &= ~FieldFlag_no_alias; // Remove the flag
}
}
if (p->flags&FieldFlag_c_vararg) {
if (p->type == NULL ||
p->type->kind != AstNode_Ellipsis) {
error_node(params[i], "`#c_vararg` can only be applied to variadic type fields");
p->flags &= ~FieldFlag_c_vararg; // Remove the flag
} else {
is_c_vararg = true;
}
}
for_array(j, p->names) {
AstNode *name = p->names[j];
@@ -1145,6 +1155,9 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *_params, bool *is_vari
Entity *end = variables[variable_count-1];
end->type = make_type_slice(c->allocator, end->type);
end->flags |= EntityFlag_Ellipsis;
if (is_c_vararg) {
end->flags |= EntityFlag_CVarArg;
}
}
Type *tuple = make_type_tuple(c->allocator);
@@ -1409,6 +1422,19 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
type->Proc.variadic = variadic;
type->Proc.calling_convention = pt->calling_convention;
if (param_count > 0) {
Entity *end = params->Tuple.variables[param_count-1];
if (end->flags&EntityFlag_CVarArg) {
if (pt->calling_convention == ProcCC_Odin) {
error(end->token, "Odin calling convention does not support #c_vararg");
} else if (pt->calling_convention == ProcCC_Fast) {
error(end->token, "Fast calling convention does not support #c_vararg");
} else {
type->Proc.c_vararg = true;
}
}
}
type->Proc.abi_compat_params = gb_alloc_array(c->allocator, Type *, param_count);
for (isize i = 0; i < param_count; i++) {
@@ -4865,6 +4891,15 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
if (score_) *score_ = score;
return CallArgumentError_NonVariadicExpand;
}
if (vari_expand && proc_type->Proc.c_vararg) {
if (show_error) {
error(ce->ellipsis,
"Cannot use `..` in call to a `#c_vararg` variadic procedure: `%.*s`",
LIT(ce->proc->Ident.string));
}
if (score_) *score_ = score;
return CallArgumentError_NonVariadicExpand;
}
if (operands.count == 0 && param_count_excluding_defaults == 0) {
if (score_) *score_ = score;
@@ -6566,6 +6601,9 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
if (f->flags&FieldFlag_no_alias) {
str = gb_string_appendc(str, "#no_alias ");
}
if (f->flags&FieldFlag_c_vararg) {
str = gb_string_appendc(str, "#c_vararg ");
}
for_array(i, f->names) {
AstNode *name = f->names[i];
+3
View File
@@ -43,6 +43,9 @@ enum EntityFlag {
EntityFlag_Value = 1<<9,
EntityFlag_Sret = 1<<10,
EntityFlag_BitFieldValue = 1<<11,
EntityFlag_CVarArg = 1<<20,
};
// Zero value means the overloading process is not yet done
+33 -4
View File
@@ -1477,7 +1477,11 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, irValue **args, isize arg_
Type *results = pt->Proc.results;
isize param_count = pt->Proc.param_count;
GB_ASSERT(param_count == arg_count);
if (pt->Proc.c_vararg) {
GB_ASSERT(param_count-1 <= arg_count);
} else {
GB_ASSERT(param_count == arg_count);
}
for (isize i = 0; i < param_count; i++) {
Type *original_type = pt->Proc.params->Tuple.variables[i]->type;
Type *new_type = pt->Proc.abi_compat_params[i];
@@ -4649,6 +4653,7 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
irValue **args = gb_alloc_array(proc->module->allocator, irValue *, gb_max(type->param_count, arg_count));
bool variadic = type->variadic;
bool vari_expand = ce->ellipsis.pos.line != 0;
bool is_c_vararg = type->c_vararg;
for_array(i, ce->args) {
irValue *a = ir_build_expr(proc, ce->args[i]);
@@ -4683,7 +4688,26 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
}
if (variadic) {
if (is_c_vararg) {
GB_ASSERT(variadic);
GB_ASSERT(!vari_expand);
isize i = 0;
for (; i < type->param_count-1; i++) {
args[i] = ir_emit_conv(proc, args[i], pt->variables[i]->type);
}
Type *variadic_type = pt->variables[i]->type;
GB_ASSERT(is_type_slice(variadic_type));
variadic_type = base_type(variadic_type)->Slice.elem;
if (!is_type_any(variadic_type)) {
for (; i < arg_count; i++) {
args[i] = ir_emit_conv(proc, args[i], variadic_type);
}
} else {
for (; i < arg_count; i++) {
args[i] = ir_emit_conv(proc, args[i], default_type(ir_type(args[i])));
}
}
} else if (variadic) {
isize i = 0;
for (; i < type->param_count-1; i++) {
args[i] = ir_emit_conv(proc, args[i], pt->variables[i]->type);
@@ -4702,7 +4726,12 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
}
}
if (variadic && !vari_expand) {
i64 final_count = type->param_count;
if (is_c_vararg) {
final_count = arg_count;
}
if (variadic && !vari_expand && !is_c_vararg) {
ir_emit_comment(proc, str_lit("variadic call argument generation"));
gbAllocator allocator = proc->module->allocator;
Type *slice_type = pt->variables[type->param_count-1]->type;
@@ -4727,7 +4756,7 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) {
args[arg_count-1] = ir_emit_load(proc, slice);
}
return ir_emit_call(proc, value, args, type->param_count);
return ir_emit_call(proc, value, args, final_count);
case_end;
case_ast_node(se, SliceExpr, expr);
+95 -43
View File
@@ -161,10 +161,11 @@ void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
isize count = rt->Tuple.variable_count;
ir_fprintf(f, "{");
for (isize i = 0; i < count; i++) {
Entity *e = rt->Tuple.variables[i];
if (i > 0) {
ir_fprintf(f, ", ");
}
ir_print_type(f, m, rt->Tuple.variables[i]->type);
ir_print_type(f, m, e->type);
}
ir_fprintf(f, "}");
}
@@ -172,6 +173,35 @@ void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
}
void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) {
i64 word_bits = 8*build_context.word_size;
t = base_type(t);
GB_ASSERT(is_type_proc(t));
isize param_count = t->Proc.param_count;
isize result_count = t->Proc.result_count;
ir_print_proc_results(f, m, t);
ir_fprintf(f, " (");
if (t->Proc.return_by_pointer) {
ir_print_type(f, m, reduce_tuple_to_single_type(t->Proc.results));
ir_fprintf(f, "* sret noalias ");
if (param_count > 0) {
ir_fprintf(f, ", ");
}
}
for (isize i = 0; i < param_count; i++) {
if (i > 0) {
ir_fprintf(f, ", ");
}
if (i+1 == param_count && t->Proc.c_vararg) {
ir_fprintf(f, "...");
} else {
ir_print_type(f, m, t->Proc.abi_compat_params[i]);
}
}
ir_fprintf(f, ")");
}
void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
i64 word_bits = 8*build_context.word_size;
GB_ASSERT_NOT_NULL(t);
@@ -328,24 +358,8 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
}
return;
case Type_Proc: {
isize param_count = t->Proc.param_count;
isize result_count = t->Proc.result_count;
ir_print_proc_results(f, m, t);
ir_fprintf(f, " (");
if (t->Proc.return_by_pointer) {
ir_print_type(f, m, reduce_tuple_to_single_type(t->Proc.results));
ir_fprintf(f, "* sret noalias ");
if (param_count > 0) {
ir_fprintf(f, ", ");
}
}
for (isize i = 0; i < param_count; i++) {
if (i > 0) {
ir_fprintf(f, ", ");
}
ir_print_type(f, m, t->Proc.abi_compat_params[i]);
}
ir_fprintf(f, ")*");
ir_print_proc_type_without_pointer(f, m, t);
ir_fprintf(f, "*");
} return;
case Type_Map: {
@@ -1216,13 +1230,16 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
irInstrCall *call = &instr->Call;
Type *proc_type = base_type(ir_type(call->value));
GB_ASSERT(is_type_proc(proc_type));
bool is_c_vararg = proc_type->Proc.c_vararg;
Type *result_type = call->type;
if (result_type) {
ir_fprintf(f, "%%%d = ", value->index);
}
ir_fprintf(f, "call ");
ir_print_calling_convention(f, m, proc_type->Proc.calling_convention);
if (result_type && !proc_type->Proc.return_by_pointer) {
if (is_c_vararg) {
ir_print_proc_type_without_pointer(f, m, proc_type);
} else if (result_type && !proc_type->Proc.return_by_pointer) {
ir_print_proc_results(f, m, proc_type);
} else {
ir_fprintf(f, "void");
@@ -1242,24 +1259,55 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
}
}
if (call->arg_count > 0) {
Type *proc_type = base_type(ir_type(call->value));
GB_ASSERT(proc_type->kind == Type_Proc);
TypeTuple *params = &proc_type->Proc.params->Tuple;
for (isize i = 0; i < call->arg_count; i++) {
Entity *e = params->variables[i];
GB_ASSERT(e != NULL);
Type *t = proc_type->Proc.abi_compat_params[i];
if (i > 0) {
ir_fprintf(f, ", ");
if (proc_type->Proc.c_vararg) {
isize i = 0;
for (; i < params->variable_count-1; i++) {
Entity *e = params->variables[i];
GB_ASSERT(e != NULL);
Type *t = proc_type->Proc.abi_compat_params[i];
if (i > 0) {
ir_fprintf(f, ", ");
}
ir_print_type(f, m, t);
if (e->flags&EntityFlag_NoAlias) {
ir_fprintf(f, " noalias");
}
ir_fprintf(f, " ");
irValue *arg = call->args[i];
ir_print_value(f, m, arg, t);
}
ir_print_type(f, m, t);
if (e->flags&EntityFlag_NoAlias) {
ir_fprintf(f, " noalias");
for (; i < call->arg_count; i++) {
if (i > 0) {
ir_fprintf(f, ", ");
}
irValue *arg = call->args[i];
Type *t = ir_type(arg);
ir_print_type(f, m, t);
ir_fprintf(f, " ");
ir_print_value(f, m, arg, t);
}
} else {
for (isize i = 0; i < call->arg_count; i++) {
Entity *e = params->variables[i];
GB_ASSERT(e != NULL);
irValue *arg = call->args[i];
Type *t = proc_type->Proc.abi_compat_params[i];
if (i > 0) {
ir_fprintf(f, ", ");
}
ir_print_type(f, m, t);
if (e->flags&EntityFlag_NoAlias) {
ir_fprintf(f, " noalias");
}
ir_fprintf(f, " ");
ir_print_value(f, m, arg, t);
}
ir_fprintf(f, " ");
irValue *arg = call->args[i];
ir_print_value(f, m, arg, t);
}
}
ir_fprintf(f, ")\n");
@@ -1495,17 +1543,21 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
if (i > 0) {
ir_fprintf(f, ", ");
}
ir_print_type(f, m, abi_type);
if (e->flags&EntityFlag_NoAlias) {
ir_fprintf(f, " noalias");
}
if (proc->body != NULL) {
if (e->token.string != "" &&
e->token.string != "_") {
ir_fprintf(f, " ");
ir_print_encoded_local(f, e->token.string);
} else {
ir_fprintf(f, " %%_.param_%td", i);
if (i+1 == params->variable_count && proc_type->c_vararg) {
ir_fprintf(f, " ...");
} else {
ir_print_type(f, m, abi_type);
if (e->flags&EntityFlag_NoAlias) {
ir_fprintf(f, " noalias");
}
if (proc->body != NULL) {
if (e->token.string != "" &&
e->token.string != "_") {
ir_fprintf(f, " ");
ir_print_encoded_local(f, e->token.string);
} else {
ir_fprintf(f, " %%_.param_%td", i);
}
}
}
}
+18 -3
View File
@@ -65,6 +65,7 @@ enum ProcTag {
ProcTag_bounds_check = 1<<0,
ProcTag_no_bounds_check = 1<<1,
ProcTag_require_results = 1<<4,
ProcTag_foreign = 1<<10,
@@ -72,6 +73,7 @@ enum ProcTag {
ProcTag_link_name = 1<<12,
ProcTag_inline = 1<<13,
ProcTag_no_inline = 1<<14,
// ProcTag_dll_import = 1<<15,
// ProcTag_dll_export = 1<<16,
};
@@ -100,8 +102,9 @@ enum FieldFlag {
FieldFlag_ellipsis = 1<<0,
FieldFlag_using = 1<<1,
FieldFlag_no_alias = 1<<2,
FieldFlag_c_vararg = 1<<3,
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias,
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg,
};
enum StmtAllowFlag {
@@ -2999,7 +3002,7 @@ AstNode *parse_proc_type(AstFile *f, Token proc_token, String *link_name_) {
parse_proc_tags(f, &tags, &link_name, &cc);
if (link_name_) *link_name_ = link_name;
if (link_name_) *link_name_ = link_name;
return ast_proc_type(f, proc_token, params, results, tags, cc);
}
@@ -3030,6 +3033,7 @@ enum FieldPrefixKind {
FieldPrefix_Using,
FieldPrefix_NoAlias,
FieldPrefix_CVarArg,
};
FieldPrefixKind is_token_field_prefix(AstFile *f) {
@@ -3047,6 +3051,9 @@ FieldPrefixKind is_token_field_prefix(AstFile *f) {
if (f->curr_token.string == "no_alias") {
return FieldPrefix_NoAlias;
}
if (f->curr_token.string == "c_vararg") {
return FieldPrefix_CVarArg;
}
break;
}
} break;
@@ -3058,6 +3065,7 @@ FieldPrefixKind is_token_field_prefix(AstFile *f) {
u32 parse_field_prefixes(AstFile *f) {
i32 using_count = 0;
i32 no_alias_count = 0;
i32 c_vararg_count = 0;
for (;;) {
FieldPrefixKind kind = is_token_field_prefix(f);
@@ -3067,15 +3075,18 @@ u32 parse_field_prefixes(AstFile *f) {
switch (kind) {
case FieldPrefix_Using: using_count += 1; next_token(f); break;
case FieldPrefix_NoAlias: no_alias_count += 1; next_token(f); break;
case FieldPrefix_CVarArg: c_vararg_count += 1; next_token(f); break;
}
}
if (using_count > 1) syntax_error(f->curr_token, "Multiple `using` in this field list");
if (no_alias_count > 1) syntax_error(f->curr_token, "Multiple `#no_alias` in this field list");
if (c_vararg_count > 1) syntax_error(f->curr_token, "Multiple `#c_vararg` in this field list");
u32 field_flags = 0;
if (using_count > 0) field_flags |= FieldFlag_using;
if (no_alias_count > 0) field_flags |= FieldFlag_no_alias;
if (c_vararg_count > 0) field_flags |= FieldFlag_c_vararg;
return field_flags;
}
@@ -3090,9 +3101,13 @@ u32 check_field_prefixes(AstFile *f, isize name_count, u32 allowed_flags, u32 se
set_flags &= ~FieldFlag_using;
}
if ((allowed_flags&FieldFlag_no_alias) == 0 && (set_flags&FieldFlag_no_alias)) {
syntax_error(f->curr_token, "`no_alias` is not allowed within this field list");
syntax_error(f->curr_token, "`#no_alias` is not allowed within this field list");
set_flags &= ~FieldFlag_no_alias;
}
if ((allowed_flags&FieldFlag_c_vararg) == 0 && (set_flags&FieldFlag_c_vararg)) {
syntax_error(f->curr_token, "`#c_vararg` is not allowed within this field list");
set_flags &= ~FieldFlag_c_vararg;
}
return set_flags;
}
+8 -1
View File
@@ -142,6 +142,7 @@ struct TypeRecord {
Type * abi_compat_result_type; \
bool variadic; \
bool require_results; \
bool c_vararg; \
ProcCallingConvention calling_convention; \
}) \
TYPE_KIND(Map, struct { \
@@ -1070,7 +1071,9 @@ bool are_types_identical(Type *x, Type *y) {
if (y->kind == Type_Tuple) {
if (x->Tuple.variable_count == y->Tuple.variable_count) {
for (isize i = 0; i < x->Tuple.variable_count; i++) {
if (!are_types_identical(x->Tuple.variables[i]->type, y->Tuple.variables[i]->type)) {
Entity *xe = x->Tuple.variables[i];
Entity *ye = y->Tuple.variables[i];
if (!are_types_identical(xe->type, ye->type)) {
return false;
}
}
@@ -1082,6 +1085,7 @@ bool are_types_identical(Type *x, Type *y) {
case Type_Proc:
if (y->kind == Type_Proc) {
return x->Proc.calling_convention == y->Proc.calling_convention &&
x->Proc.c_vararg == y->Proc.c_vararg &&
x->Proc.variadic == y->Proc.variadic &&
are_types_identical(x->Proc.params, y->Proc.params) &&
are_types_identical(x->Proc.results, y->Proc.results);
@@ -2292,6 +2296,9 @@ gbString write_type_to_string(gbString str, Type *type) {
if (i > 0) {
str = gb_string_appendc(str, ", ");
}
if (var->flags&EntityFlag_CVarArg) {
str = gb_string_appendc(str, "#c_vararg ");
}
if (var->flags&EntityFlag_Ellipsis) {
Type *slice = base_type(var->type);
str = gb_string_appendc(str, "..");