}
}
+#tag conv.Custom_Format.{
+ #solidify format_map {K=Key_Type, V=Value_Type}
+}
Map :: struct (Key_Type: type_expr, Value_Type: type_expr) where ValidKey(Key_Type) {
- #struct_tag conv.Custom_Format.{ #solidify format_map {K=Key_Type, V=Value_Type} };
-
allocator : Allocator;
hashes : [] i32;
--- /dev/null
+
+package runtime.info
+
+tagged_procedures: [] ^Tagged_Procedure
+
+Tagged_Procedure :: struct {
+ // This should be cast to the correct function type.
+ // i.e. *cast(^(i32, i32) -> i32) ^tagged_procedures[0].func;
+ func: () -> void;
+ type: type_expr;
+ tags: [] any;
+}
#load "./runtime/common"
#load "./runtime/info/helper"
-#load "./runtime/info/types"
-#load "./runtime/info/foreign_blocks"
#load "./arg_parse"
ConstraintContext constraints;
+ bh_arr(AstTyped *) tags;
+
// Polymorphic procedures use the following fields
Scope *parent_scope_of_poly_proc;
bh_arr(AstPolyParam) poly_params;
extern AstTyped *type_table_node;
extern AstTyped *foreign_blocks_node;
extern AstType *foreign_block_type;
+extern AstTyped *tagged_procedures_node;
extern AstFunction *builtin_initialize_data_segments;
extern AstFunction *builtin_run_init_procedures;
extern bh_arr(AstFunction *) init_procedures;
func->poly_scope = NULL;
func->entity = NULL;
func->type = NULL;
+ func->tags = NULL;
}
static inline void convert_function_to_polyproc(AstFunction *func) {
bh_arr(AstFlags) scope_flags;
+ bh_arr(AstTyped *) stored_tags;
+
b32 hit_unexpected_token : 1;
b32 parse_calls : 1;
bh_arr(AstForeignBlock *) foreign_blocks;
u32 next_foreign_block_idx;
+ bh_arr(AstFunction *) procedures_with_tags;
+
// NOTE: Used internally as a map from strings that represent function types,
// 0x7f 0x7f : 0x7f ( (i32, i32) -> i32 )
// to the function type index if it has been created.
AstTyped *type_table_node = NULL;
AstTyped *foreign_blocks_node = NULL;
AstType *foreign_block_type = NULL;
+AstTyped *tagged_procedures_node = NULL;
AstFunction *builtin_initialize_data_segments = NULL;
AstFunction *builtin_run_init_procedures = NULL;
bh_arr(AstFunction *) init_procedures = NULL;
type_table_node = (AstTyped *) symbol_raw_resolve(p->scope, "type_table");
foreign_blocks_node = (AstTyped *) symbol_raw_resolve(p->scope, "foreign_blocks");
foreign_block_type = (AstType *) symbol_raw_resolve(p->scope, "foreign_block");
+ tagged_procedures_node = (AstTyped *) symbol_raw_resolve(p->scope, "tagged_procedures");
}
fori (i, 0, Binary_Op_Count) {
if (func->entity_header && func->entity_header->state < Entity_State_Code_Gen)
YIELD(func->token->pos, "Waiting for procedure header to pass type-checking");
+ bh_arr_each(AstTyped *, pexpr, func->tags) {
+ CHECK(expression, pexpr);
+
+ if (((*pexpr)->flags & Ast_Flag_Comptime) == 0) {
+ ERROR((*pexpr)->token->pos, "#tag expressions should be compile time known.");
+ }
+ }
+
inside_for_iterator = 0;
expected_return_type = &func->type->Function.return_type;
if (func->body) {
captured_entities = NULL;
df->params = NULL;
- bh_arr_new(global_heap_allocator, df->params, bh_arr_length(sf->params));
+ bh_arr_new(context.ast_alloc, df->params, bh_arr_length(sf->params));
bh_arr_each(AstParam, param, sf->params) {
AstParam new_param = { 0 };
if (sf->constraints.constraints) {
memset(&df->constraints, 0, sizeof(ConstraintContext));
- bh_arr_new(global_heap_allocator, df->constraints.constraints, bh_arr_length(sf->constraints.constraints));
+ bh_arr_new(context.ast_alloc, df->constraints.constraints, bh_arr_length(sf->constraints.constraints));
bh_arr_each(AstConstraint *, constraint, sf->constraints.constraints) {
bh_arr_push(df->constraints.constraints, (AstConstraint *) ast_clone(a, (AstNode *) *constraint));
}
}
+ if (sf->tags) {
+ bh_arr_new(context.ast_alloc, df->tags, bh_arr_length(sf->tags));
+ bh_arr_each(AstTyped *, pexpr, sf->tags) {
+ bh_arr_push(df->tags, (AstTyped *) ast_clone(a, (AstNode *) *pexpr));
+ }
+ }
+
break;
}
.package = NULL,
.include = create_load(context.ast_alloc, "core/runtime/info/foreign_blocks"),
}));
+ entity_heap_insert(&context.entities, ((Entity) {
+ .state = Entity_State_Parse_Builtin,
+ .type = Entity_Type_Load_File,
+ .package = NULL,
+ .include = create_load(context.ast_alloc, "core/runtime/info/proc_tags"),
+ }));
entity_heap_insert(&context.entities, ((Entity) {
.state = Entity_State_Parse_Builtin,
}
+static void expect_no_stored_tags_pos(OnyxParser *parser, OnyxFilePos pos) {
+ if (bh_arr_length(parser->stored_tags) > 0) {
+ onyx_report_error(pos, Error_Critical, "#tag is not allowed on this element.");
+ parser->hit_unexpected_token = 1;
+ }
+}
+
+static void expect_no_stored_tags(OnyxParser *parser) {
+ expect_no_stored_tags_pos(parser, parser->curr->pos);
+}
+
+static void flush_stored_tags(OnyxParser *parser, bh_arr(AstTyped *) *out_arr) {
+ if (bh_arr_length(parser->stored_tags) == 0) return;
+
+ bh_arr(AstTyped *) arr = *out_arr;
+
+ if (arr == NULL) {
+ bh_arr_new(parser->allocator, arr, bh_arr_length(parser->stored_tags));
+ }
+
+ bh_arr_each(AstTyped *, pexpr, parser->stored_tags) {
+ bh_arr_push(arr, *pexpr);
+ }
+
+ bh_arr_clear(parser->stored_tags);
+
+ *out_arr = arr;
+}
+
+
// TODO: Make parsing numeric literals not rely on the C standard libary.
static AstNumLit* parse_int_literal(OnyxParser* parser) {
AstNumLit* int_node = make_node(AstNumLit, Ast_Kind_NumLit);
s_node = make_node(AstStructType, Ast_Kind_Struct_Type);
s_node->token = s_token;
+ flush_stored_tags(parser, &s_node->meta_tags);
+
// Parse polymorphic parameters
if (consume_token_if_next(parser, '(')) {
bh_arr(AstPolyStructParam) poly_params = NULL;
}
}
- bh_arr(AstTyped *) struct_meta_tags=NULL;
-
expect_token(parser, '{');
b32 member_is_used = 0;
while (!consume_token_if_next(parser, '}')) {
if (parser->hit_unexpected_token) return s_node;
- if (parse_possible_directive(parser, "struct_tag")) {
- if (struct_meta_tags == NULL) bh_arr_new(global_heap_allocator, struct_meta_tags, 1);
- AstTyped* expr = parse_expression(parser, 0);
- bh_arr_push(struct_meta_tags, expr);
-
- consume_token_if_next(parser, ';');
- continue;
- }
-
if (parse_possible_directive(parser, "persist")) {
struct_type_create_scope(parser, s_node);
expect_token(parser, ';');
}
- s_node->meta_tags = struct_meta_tags;
if (s_node->scope) parser->current_scope = parser->current_scope->parent;
bh_arr_free(member_list_temp);
func_def->token = token;
bh_arr_push(parser->current_function_stack, func_def);
+ flush_stored_tags(parser, &func_def->tags);
+
bh_arr_new(global_heap_allocator, func_def->params, 4);
bh_arr(AstPolyParam) polymorphic_vars = NULL;
}
static AstTyped* parse_global_declaration(OnyxParser* parser) {
+ expect_no_stored_tags(parser);
+
AstGlobal* global_node = make_node(AstGlobal, Ast_Kind_Global);
global_node->token = expect_token(parser, Token_Type_Keyword_Global);
}
static AstEnumType* parse_enum_declaration(OnyxParser* parser) {
+ expect_no_stored_tags(parser);
+
AstEnumType* enum_node = make_node(AstEnumType, Ast_Kind_Enum_Type);
enum_node->token = expect_token(parser, Token_Type_Keyword_Enum);
bh_arr_new(global_heap_allocator, enum_node->values, 4);
while (parser->curr->type == '#') {
+ if (parser->hit_unexpected_token) return enum_node;
+
if (parse_possible_directive(parser, "flags")) {
enum_node->is_flags = 1;
} else {
}
static AstIf* parse_static_if_stmt(OnyxParser* parser, b32 parse_block_as_statements) {
+ expect_no_stored_tags(parser);
+
AstIf* static_if_node = make_node(AstIf, Ast_Kind_Static_If);
static_if_node->token = expect_token(parser, '#');
static_if_node->defined_in_scope = parser->current_scope;
static AstMemRes* parse_memory_reservation(OnyxParser* parser, OnyxToken* symbol, b32 threadlocal) {
expect_token(parser, ':');
+ expect_no_stored_tags(parser);
+
AstMemRes* memres = make_node(AstMemRes, Ast_Kind_Memres);
memres->threadlocal = threadlocal;
memres->token = symbol;
}
static AstMacro* parse_macro(OnyxParser* parser) {
+ expect_no_stored_tags(parser);
+
AstMacro* macro = make_node(AstMacro, Ast_Kind_Macro);
macro->token = expect_token(parser, Token_Type_Keyword_Macro);
}
static AstBinding* parse_top_level_binding(OnyxParser* parser, OnyxToken* symbol) {
- expect_token(parser, ':');
+ OnyxToken *after_second_colon = expect_token(parser, ':');
+ if (after_second_colon) after_second_colon += 1;
AstTyped* node = parse_top_level_expression(parser);
if (parser->hit_unexpected_token || node == NULL)
binding->token = symbol;
binding->node = (AstNode *) node;
+ if (after_second_colon) expect_no_stored_tags_pos(parser, after_second_colon->pos);
return binding;
}
ENTITY_SUBMIT(library);
return;
}
+ else if (parse_possible_directive(parser, "tag")) {
+ AstTyped *expr = parse_expression(parser, 0);
+ bh_arr_push(parser->stored_tags, expr);
+ return;
+ }
else {
OnyxToken* directive_token = expect_token(parser, '#');
OnyxToken* symbol_token = expect_token(parser, Token_Type_Symbol);
parser.current_symbol_stack = NULL;
parser.current_function_stack = NULL;
parser.scope_flags = NULL;
+ parser.stored_tags = NULL;
parser.parse_calls = 1;
parser.polymorph_context = (PolymorphicContext) {
bh_arr_new(global_heap_allocator, parser.alternate_entity_placement_stack, 4);
bh_arr_new(global_heap_allocator, parser.current_symbol_stack, 4);
bh_arr_new(global_heap_allocator, parser.scope_flags, 4);
+ bh_arr_new(global_heap_allocator, parser.stored_tags, 4);
return parser;
}
}
}
+ bh_arr_each(AstTyped *, pexpr, func->tags) {
+ SYMRES(expression, pexpr);
+ }
+
func->flags |= Ast_Flag_Has_Been_Symres;
}
}
+static inline b32 should_emit_function(AstFunction* fd) {
+ // NOTE: Don't output intrinsic functions
+ if (fd->is_intrinsic) return 0;
+
+ // NOTE: Don't output functions that are not used, only if
+ // they are also not exported.
+ if ((fd->flags & Ast_Flag_Function_Used) == 0) {
+ if (fd->is_exported || bh_arr_length(fd->tags) > 0) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
typedef enum StructuredBlockType StructuredBlockType;
enum StructuredBlockType {
SBT_Basic_Block, // Cannot be targeted using jump
}
}
-static inline b32 should_emit_function(AstFunction* fd) {
- // NOTE: Don't output intrinsic functions
- if (fd->is_intrinsic) return 0;
-
- // NOTE: Don't output functions that are not used, only if
- // they are also not exported.
- if ((fd->flags & Ast_Flag_Function_Used) == 0) {
- if (fd->is_exported) {
- return 1;
- } else {
- return 0;
- }
- }
-
- return 1;
-}
-
static void emit_function(OnyxWasmModule* mod, AstFunction* fd) {
if (!should_emit_function(fd)) return;
return;
}
+ if (tagged_procedures_node != NULL && (AstMemRes *) tagged_procedures_node == memres) {
+ u64 tagged_procedures_location = build_tagged_procedures(mod);
+ memres->addr = tagged_procedures_location;
+
+ return;
+ }
+
if (memres->threadlocal) {
memres->addr = mod->next_tls_offset;
bh_align(memres->addr, alignment);
.foreign_blocks = NULL,
.next_foreign_block_idx = 0,
+
+ .procedures_with_tags = NULL
};
bh_arena* eid = bh_alloc(global_heap_allocator, sizeof(bh_arena));
bh_arr_new(global_heap_allocator, module.local_allocations, 4);
bh_arr_new(global_heap_allocator, module.stack_leave_patches, 4);
bh_arr_new(global_heap_allocator, module.foreign_blocks, 4);
+ bh_arr_new(global_heap_allocator, module.procedures_with_tags, 4);
if (context.options->use_multi_threading) {
WasmImport mem_import = {
if (ent->function->flags & Ast_Flag_Proc_Is_Null) {
if (module->null_proc_func_idx == -1) module->null_proc_func_idx = get_element_idx(module, ent->function);
}
+
+ if (ent->function->tags != NULL) {
+ bh_arr_push(module->procedures_with_tags, ent->function);
+ }
break;
case Entity_Type_Global_Header:
u32 data_loc;
} StructMethodData;
-u64 build_type_table(OnyxWasmModule* module) {
+static u64 build_type_table(OnyxWasmModule* module) {
bh_arr(u32) base_patch_locations=NULL;
bh_arr_new(global_heap_allocator, base_patch_locations, 256);
-static b32 build_foreign_blocks(OnyxWasmModule* module) {
+static u64 build_foreign_blocks(OnyxWasmModule* module) {
bh_arr(u32) base_patch_locations=NULL;
bh_arr_new(global_heap_allocator, base_patch_locations, 256);
return global_data_ptr;
+#undef WRITE_SLICE
+#undef WRITE_PTR
#undef PATCH
-}
\ No newline at end of file
+}
+
+
+
+static u64 build_tagged_procedures(OnyxWasmModule *module) {
+ bh_arr(u32) base_patch_locations=NULL;
+ bh_arr_new(global_heap_allocator, base_patch_locations, 256);
+
+#define PATCH (bh_arr_push(base_patch_locations, tag_proc_buffer.length))
+#define WRITE_PTR(val) \
+ bh_buffer_align(&tag_proc_buffer, POINTER_SIZE); \
+ PATCH; \
+ if (POINTER_SIZE == 4) bh_buffer_write_u32(&tag_proc_buffer, val); \
+ if (POINTER_SIZE == 8) bh_buffer_write_u64(&tag_proc_buffer, val);
+#define WRITE_SLICE(ptr, count) \
+ WRITE_PTR(ptr); \
+ if (POINTER_SIZE == 4) bh_buffer_write_u32(&tag_proc_buffer, count); \
+ if (POINTER_SIZE == 8) bh_buffer_write_u64(&tag_proc_buffer, count);
+
+ #if (POINTER_SIZE == 4)
+ #define Tagged_Procedure_Type u32
+ #else
+ #define Tagged_Procedure_Type u64
+ #endif
+ u32 proc_count = bh_arr_length(module->procedures_with_tags);
+ Tagged_Procedure_Type* tag_proc_info = bh_alloc_array(global_heap_allocator, Tagged_Procedure_Type, proc_count); // HACK
+ memset(tag_proc_info, 0, proc_count * sizeof(Tagged_Procedure_Type));
+
+ bh_buffer tag_proc_buffer;
+ bh_buffer_init(&tag_proc_buffer, global_heap_allocator, 4096);
+
+ //
+ // This is necessary because 0 is an invalid offset to store in this
+ // buffer, as 0 will map to NULL. This could be a single byte insertion,
+ // but 64 bytes keeps better alignment.
+ bh_buffer_write_u64(&tag_proc_buffer, 0);
+
+ u32 index = 0;
+ bh_arr_each(AstFunction *, pfunc, module->procedures_with_tags) {
+ AstFunction *func = *pfunc;
+ if (!should_emit_function(func)) {
+ proc_count--;
+ continue;
+ }
+
+ u32 tag_count = bh_arr_length(func->tags);
+ u32 *tag_data_offsets = bh_alloc_array(global_scratch_allocator, u32, tag_count);
+ u32 *tag_data_types = bh_alloc_array(global_scratch_allocator, u32, tag_count);
+
+ u32 tag_index = 0;
+ bh_arr_each(AstTyped *, ptag, func->tags) {
+ AstTyped *tag = *ptag;
+ bh_buffer_align(&tag_proc_buffer, type_alignment_of(tag->type));
+
+ tag_data_offsets[tag_index ] = tag_proc_buffer.length;
+ tag_data_types [tag_index++] = tag->type->id;
+
+ u32 size = type_size_of(tag->type);
+ bh_buffer_grow(&tag_proc_buffer, tag_proc_buffer.length + size);
+ u8* buffer = tag_proc_buffer.data + tag_proc_buffer.length;
+ emit_raw_data(module, buffer, tag);
+ tag_proc_buffer.length += size;
+ }
+
+ bh_buffer_align(&tag_proc_buffer, 4);
+ u32 tag_array_base = tag_proc_buffer.length;
+ fori (i, 0, tag_count) {
+ PATCH;
+ bh_buffer_write_u32(&tag_proc_buffer, tag_data_offsets[i]);
+ bh_buffer_write_u32(&tag_proc_buffer, tag_data_types[i]);
+ }
+
+ bh_buffer_align(&tag_proc_buffer, 4);
+ tag_proc_info[index++] = tag_proc_buffer.length;
+
+ bh_buffer_write_u32(&tag_proc_buffer, get_element_idx(module, func));
+ bh_buffer_write_u32(&tag_proc_buffer, func->type->id);
+ WRITE_SLICE(tag_array_base, tag_count);
+ }
+
+ if (context.options->verbose_output == 1) {
+ bh_printf("Foreign blocks size: %d bytes.\n", tag_proc_buffer.length);
+ }
+
+ u32 offset = module->next_datum_offset;
+ bh_align(offset, 8);
+
+ u64 tagged_procedures_location = offset;
+
+ WasmDatum tagged_procedures_data = {
+ .offset = offset,
+ .length = proc_count * POINTER_SIZE,
+ .data = tag_proc_info,
+ };
+ bh_arr_push(module->data, tagged_procedures_data);
+
+ offset += tagged_procedures_data.length;
+
+ fori (i, 0, proc_count) {
+ tag_proc_info[i] += offset;
+ }
+
+ bh_arr_each(u32, patch_loc, base_patch_locations) {
+ if (POINTER_SIZE == 4) {
+ u32* loc = bh_pointer_add(tag_proc_buffer.data, *patch_loc);
+ if (*loc == 0) continue;
+
+ *loc += offset;
+ }
+ if (POINTER_SIZE == 8) {
+ u64* loc = bh_pointer_add(tag_proc_buffer.data, *patch_loc);
+ if (*loc == 0) continue;
+
+ *loc += offset;
+ }
+ }
+
+ WasmDatum tagged_procedures_info_data = {
+ .offset = offset,
+ .length = tag_proc_buffer.length,
+ .data = tag_proc_buffer.data,
+ };
+ bh_arr_push(module->data, tagged_procedures_info_data);
+ offset += tagged_procedures_info_data.length;
+
+ u64 global_data_ptr = offset;
+
+ Tagged_Procedure_Type* tmp_data = bh_alloc(global_heap_allocator, 2 * POINTER_SIZE);
+ tmp_data[0] = tagged_procedures_location;
+ tmp_data[1] = proc_count;
+ WasmDatum tagged_procedures_global_data = {
+ .offset = offset,
+ .length = 2 * POINTER_SIZE,
+ .data = tmp_data,
+ };
+ bh_arr_push(module->data, tagged_procedures_global_data);
+ offset += tagged_procedures_global_data.length;
+
+ module->next_datum_offset = offset;
+
+ return global_data_ptr;
+
+#undef WRITE_SLICE
+#undef WRITE_PTR
+#undef PATCH
+}
+
{ t ^ t } -> T;
}
+#tag conv.Custom_Format.{ Complex.format }
Complex :: struct {
x, y: f32;
- #struct_tag conv.Custom_Format.{ format }
format :: (output: ^conv.Format_Output, format: ^conv.Format, c: ^Complex) {
conv.format(output, "{.2} + {.2}i", c.x, c.y);
}