function pointers and callbacks
This commit is contained in:
+436
-29
@@ -489,6 +489,71 @@ call_arg_expected :: proc(function: ast.Function, index: int) -> types.Type {
|
||||
return type_from_syntax(function.params[index].type)
|
||||
}
|
||||
|
||||
callable_arg_expected :: proc(function_type: types.Type, function_item: types.Node, store: ^types.Store, index: int) -> types.Type {
|
||||
if index < 0 || index >= int(function_item.field_count) {
|
||||
return types.INVALID
|
||||
}
|
||||
params := types.params_for(store, function_type)
|
||||
if index >= len(params) {
|
||||
return types.INVALID
|
||||
}
|
||||
return params[index].type
|
||||
}
|
||||
|
||||
valid_callable_arity :: proc(function_item: types.Node, count: int) -> bool {
|
||||
return count >= int(function_item.field_count) if function_item.variadic else count == int(function_item.field_count)
|
||||
}
|
||||
|
||||
function_value_signature :: proc(
|
||||
checker: ^Checker,
|
||||
template: ast.Function_Id,
|
||||
) -> (params: []types.Type, result: types.Type, ok: bool) {
|
||||
if template == ast.INVALID_FUNCTION || int(template) >= len(checker.ast_module.functions) {
|
||||
return nil, types.INVALID, false
|
||||
}
|
||||
function := checker.ast_module.functions[template]
|
||||
if !function.c_abi {
|
||||
return nil, types.INVALID, false
|
||||
}
|
||||
result = type_from_syntax(function.result)
|
||||
if !types.is_void(result) && !is_runtime_type(checker, result) {
|
||||
return nil, types.INVALID, false
|
||||
}
|
||||
params = make([]types.Type, len(function.params), checker.allocator)
|
||||
for param, index in function.params {
|
||||
param_type := type_from_syntax(param.type)
|
||||
if !is_runtime_type(checker, param_type) {
|
||||
delete(params, checker.allocator)
|
||||
return nil, types.INVALID, false
|
||||
}
|
||||
params[index] = param_type
|
||||
}
|
||||
return params, result, true
|
||||
}
|
||||
|
||||
function_pointer_type_for_template :: proc(
|
||||
checker: ^Checker,
|
||||
template: ast.Function_Id,
|
||||
demanded: ^[dynamic]Spec_Id = nil,
|
||||
) -> (types.Type, Spec_Id, bool) {
|
||||
params, result, ok := function_value_signature(checker, template)
|
||||
if !ok {
|
||||
return types.INVALID, INVALID_SPEC, false
|
||||
}
|
||||
defer delete(params, checker.allocator)
|
||||
function := checker.ast_module.functions[template]
|
||||
function_type := types.function(&checker.module.types, params, result, true, function.variadic)
|
||||
pointer_type := types.pointer(&checker.module.types, function_type, false, true)
|
||||
spec := INVALID_SPEC
|
||||
if demanded == nil {
|
||||
spec = ensure_spec(checker, template, params)
|
||||
} else {
|
||||
spec = find_spec(checker, template, params)
|
||||
mark_spec_demanded(checker, spec, demanded)
|
||||
}
|
||||
return pointer_type, spec, spec != INVALID_SPEC
|
||||
}
|
||||
|
||||
contains_name :: proc(names: []symbol.Id, name: symbol.Id) -> bool {
|
||||
for existing in names {
|
||||
if existing == name {
|
||||
@@ -518,6 +583,9 @@ mark_expr_imports_used :: proc(checker: ^Checker, expr_id: ast.Expr_Id, file: as
|
||||
switch expr.kind {
|
||||
case .Call:
|
||||
append(&stack, ..expr.args)
|
||||
if expr.left != ast.INVALID_EXPR {
|
||||
append(&stack, expr.left)
|
||||
}
|
||||
case .Array, .Struct_Literal, .Slice:
|
||||
append(&stack, ..expr.args)
|
||||
if expr.left != ast.INVALID_EXPR {
|
||||
@@ -600,11 +668,15 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
}
|
||||
if !function.has_body && function.c_abi {
|
||||
for param in function.params {
|
||||
if add_unsupported_type_diagnostic(checker, param.span, type_from_syntax(param.type)) !=
|
||||
param_type := type_from_syntax(param.type)
|
||||
if add_unsupported_type_diagnostic(checker, param.span, param_type) !=
|
||||
source.INVALID_DIAGNOSTIC {
|
||||
continue
|
||||
}
|
||||
if !types.is_c_signature_type(type_from_syntax(param.type), &checker.module.types) {
|
||||
if types.contains_c_struct_by_value(param_type, &checker.module.types) {
|
||||
continue
|
||||
}
|
||||
if !types.is_c_signature_type(param_type, &checker.module.types) {
|
||||
checker.template_diagnostics[function_id] = source.addf(
|
||||
checker.diagnostics,
|
||||
param.span,
|
||||
@@ -615,6 +687,7 @@ validate_declarations :: proc(checker: ^Checker) {
|
||||
}
|
||||
result := type_from_syntax(function.result)
|
||||
if add_unsupported_type_diagnostic(checker, function.span, result) == source.INVALID_DIAGNOSTIC &&
|
||||
!types.contains_c_struct_by_value(result, &checker.module.types) &&
|
||||
!types.is_c_signature_type(result, &checker.module.types, true) {
|
||||
checker.template_diagnostics[function_id] = source.addf(
|
||||
checker.diagnostics,
|
||||
@@ -683,17 +756,43 @@ validate_type_nodes :: proc(checker: ^Checker) {
|
||||
)
|
||||
}
|
||||
}
|
||||
if item.kind == .Struct {
|
||||
if item.kind == .Struct || item.kind == .Union {
|
||||
if item.c_layout && !item.opaque && item.field_count == 0 {
|
||||
source.add(
|
||||
checker.diagnostics,
|
||||
source.Span{},
|
||||
"c_struct definitions require at least one field",
|
||||
)
|
||||
}
|
||||
for field in types.fields_for(&checker.module.types, id) {
|
||||
if types.contains_c_struct_by_value(field.type, &checker.module.types) {
|
||||
if !types.is_runtime_value(field.type, &checker.module.types) {
|
||||
source.add(
|
||||
checker.diagnostics,
|
||||
source.Span{},
|
||||
"C records may only appear behind pointers",
|
||||
"record fields must have runtime value types",
|
||||
)
|
||||
} else if item.c_layout && !types.is_c_record_field_type(field.type, &checker.module.types) {
|
||||
source.add(
|
||||
checker.diagnostics,
|
||||
source.Span{},
|
||||
"c_struct fields must have C-layout-compatible types",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if item.kind == .Function {
|
||||
if !item.c_abi {
|
||||
source.add(checker.diagnostics, source.Span{}, "only c_func function pointer types are supported")
|
||||
}
|
||||
for param in types.params_for(&checker.module.types, id) {
|
||||
if types.is_void(param.type) || !types.is_c_signature_type(param.type, &checker.module.types) {
|
||||
source.add(checker.diagnostics, source.Span{}, "function pointer parameters must be concrete C signature types")
|
||||
}
|
||||
}
|
||||
if !types.is_c_signature_type(item.child, &checker.module.types, true) {
|
||||
source.add(checker.diagnostics, source.Span{}, "function pointer results must be concrete C signature types or void")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -893,7 +992,8 @@ infer_compound_expr :: proc(
|
||||
_ = infer_nested_expr(checker, checker.ast_module.exprs[keyed].left, locals, pkg, file, demanded)
|
||||
}
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file)
|
||||
return types.find_named(store, u32(target_pkg), u32(expr.name)) if available else types.INVALID
|
||||
value := types.find_named(store, u32(target_pkg), u32(expr.name)) if available else types.INVALID
|
||||
return types.resolve_alias(value, store)
|
||||
case .Keyed:
|
||||
return infer_nested_expr(checker, expr.left, locals, pkg, file, demanded)
|
||||
case:
|
||||
@@ -994,6 +1094,20 @@ infer_expr :: proc(
|
||||
}
|
||||
}
|
||||
}
|
||||
if !types.is_valid(last) {
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file)
|
||||
if available {
|
||||
template := find_template(checker, expr.name, target_pkg)
|
||||
if template != ast.INVALID_FUNCTION &&
|
||||
len(checker.ast_module.functions[template].unsupported_reason) == 0 &&
|
||||
checker.template_diagnostics[template] == source.INVALID_DIAGNOSTIC {
|
||||
pointer_type, _, ok := function_pointer_type_for_template(checker, template, demanded)
|
||||
if ok {
|
||||
last = pointer_type
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = pop(&stack)
|
||||
case .Negate:
|
||||
stack[frame_index].stage = 5
|
||||
@@ -1002,14 +1116,60 @@ infer_expr :: proc(
|
||||
stack[frame_index].stage = 1
|
||||
append(&stack, Infer_Frame{expr=expr.left, template=ast.INVALID_FUNCTION})
|
||||
case .Call:
|
||||
if expr.left != ast.INVALID_EXPR {
|
||||
callee_type := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded)
|
||||
_, function_item, function_type, ok := types.function_pointer(callee_type, &checker.module.types)
|
||||
if !ok {
|
||||
last = types.INVALID
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
stack[frame_index].left = function_type
|
||||
stack[frame_index].args = make([]types.Type, len(expr.args), checker.allocator)
|
||||
stack[frame_index].stage = 6
|
||||
if len(expr.args) > 0 {
|
||||
append(&stack, Infer_Frame{expr=expr.args[0], template=ast.INVALID_FUNCTION})
|
||||
} else if valid_callable_arity(function_item, 0) {
|
||||
last = function_item.child
|
||||
delete(stack[frame_index].args, checker.allocator)
|
||||
stack[frame_index].args = nil
|
||||
_ = pop(&stack)
|
||||
}
|
||||
continue
|
||||
}
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file)
|
||||
template := ast.INVALID_FUNCTION
|
||||
if available {
|
||||
template = find_template(checker, expr.name, target_pkg)
|
||||
}
|
||||
if template == ast.INVALID_FUNCTION {
|
||||
last = types.INVALID
|
||||
_ = pop(&stack)
|
||||
callee_type := types.INVALID
|
||||
if !symbol.is_valid(expr.qualifier) {
|
||||
callee_type = find_infer_local(locals, expr.name)
|
||||
}
|
||||
if !types.is_valid(callee_type) && available {
|
||||
global := find_global(checker, expr.name, target_pkg)
|
||||
if global != ast.INVALID_GLOBAL {
|
||||
callee_type = checker.global_types[global]
|
||||
}
|
||||
}
|
||||
_, function_item, function_type, ok := types.function_pointer(callee_type, &checker.module.types)
|
||||
if !ok {
|
||||
last = types.INVALID
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
stack[frame_index].left = function_type
|
||||
stack[frame_index].args = make([]types.Type, len(expr.args), checker.allocator)
|
||||
stack[frame_index].stage = 6
|
||||
if len(expr.args) > 0 {
|
||||
append(&stack, Infer_Frame{expr=expr.args[0], template=ast.INVALID_FUNCTION})
|
||||
} else if valid_callable_arity(function_item, 0) {
|
||||
last = function_item.child
|
||||
delete(stack[frame_index].args, checker.allocator)
|
||||
stack[frame_index].args = nil
|
||||
_ = pop(&stack)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if len(checker.ast_module.functions[template].unsupported_reason) > 0 {
|
||||
@@ -1091,6 +1251,25 @@ infer_expr :: proc(
|
||||
stack[frame_index].args = nil
|
||||
_ = pop(&stack)
|
||||
}
|
||||
if frame.stage == 6 {
|
||||
if frame.arg_index < len(expr.args) {
|
||||
stack[frame_index].args[frame.arg_index] = last
|
||||
stack[frame_index].arg_index += 1
|
||||
if frame.arg_index+1 < len(expr.args) {
|
||||
append(&stack, Infer_Frame{expr=expr.args[frame.arg_index+1], template=ast.INVALID_FUNCTION})
|
||||
continue
|
||||
}
|
||||
}
|
||||
function_item, ok := types.node(&checker.module.types, frame.left)
|
||||
if ok && function_item.kind == .Function && valid_callable_arity(function_item, len(expr.args)) {
|
||||
last = function_item.child
|
||||
} else {
|
||||
last = types.INVALID
|
||||
}
|
||||
delete(stack[frame_index].args, checker.allocator)
|
||||
stack[frame_index].args = nil
|
||||
_ = pop(&stack)
|
||||
}
|
||||
}
|
||||
return last
|
||||
}
|
||||
@@ -1548,6 +1727,65 @@ find_struct_field :: proc(checker: ^Checker, struct_type: types.Type, name: symb
|
||||
return 0, {}, false
|
||||
}
|
||||
|
||||
build_function_value :: proc(
|
||||
checker: ^Checker,
|
||||
template: ast.Function_Id,
|
||||
span: source.Span,
|
||||
expected: types.Type,
|
||||
) -> hir.Expr_Id {
|
||||
if template == ast.INVALID_FUNCTION || int(template) >= len(checker.ast_module.functions) {
|
||||
return hir.INVALID_EXPR
|
||||
}
|
||||
function := checker.ast_module.functions[template]
|
||||
if len(function.unsupported_reason) > 0 {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
span,
|
||||
"C declaration '%s' is unavailable: %s",
|
||||
symbol_text(checker, function.name),
|
||||
function.unsupported_reason,
|
||||
)
|
||||
return invalid_hir_expr(checker, span, id)
|
||||
}
|
||||
if checker.template_diagnostics[template] != source.INVALID_DIAGNOSTIC {
|
||||
return invalid_hir_expr(checker, span, checker.template_diagnostics[template])
|
||||
}
|
||||
params, result, ok := function_value_signature(checker, template)
|
||||
if !ok {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
span,
|
||||
"function '%s' cannot be used as a C callback; expected a concrete c_func",
|
||||
symbol_text(checker, function.name),
|
||||
)
|
||||
return invalid_hir_expr(checker, span, id)
|
||||
}
|
||||
defer delete(params, checker.allocator)
|
||||
function_type := types.function(&checker.module.types, params, result, true, function.variadic)
|
||||
pointer_type := types.pointer(&checker.module.types, function_type, false, true)
|
||||
spec := find_spec(checker, template, params)
|
||||
if spec == INVALID_SPEC {
|
||||
id := source.addf(
|
||||
checker.diagnostics,
|
||||
span,
|
||||
"could not resolve callback specialization of '%s'",
|
||||
symbol_text(checker, function.name),
|
||||
)
|
||||
return invalid_hir_expr(checker, span, id, pointer_type)
|
||||
}
|
||||
function_id := checker.specs[spec].hir_id
|
||||
assert(function_id != hir.INVALID_FUNCTION)
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Function,
|
||||
span=span,
|
||||
type=pointer_type,
|
||||
target=hir.function_ref(function_id),
|
||||
left=hir.INVALID_EXPR,
|
||||
right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
|
||||
build_nested_expr :: proc(
|
||||
checker: ^Checker,
|
||||
expr_id: ast.Expr_Id,
|
||||
@@ -1776,16 +2014,18 @@ build_compound_expr :: proc(
|
||||
case .Struct_Literal:
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file, true)
|
||||
struct_type := types.find_named(store, u32(target_pkg), u32(expr.name)) if available else types.INVALID
|
||||
if !types.is_struct(struct_type, store) || types.is_opaque_struct(struct_type, store) {
|
||||
id := source.addf(checker.diagnostics, expr.span, "unknown or opaque struct type '%s'", symbol_text(checker, expr.name))
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
if types.is_c_struct(struct_type, store) {
|
||||
id := source.add(checker.diagnostics, expr.span, "c_struct values cannot be constructed by value")
|
||||
struct_type = types.resolve_alias(struct_type, store)
|
||||
if !types.is_record(struct_type, store) || types.is_opaque_struct(struct_type, store) {
|
||||
id := source.addf(checker.diagnostics, expr.span, "unknown or opaque record type '%s'", symbol_text(checker, expr.name))
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
fields := types.fields_for(store, struct_type)
|
||||
values := make([]hir.Expr_Id, len(fields), checker.allocator)
|
||||
union_record := types.is_union(struct_type, store)
|
||||
if union_record && len(expr.args) != 1 {
|
||||
id := source.add(checker.diagnostics, expr.span, "union literal requires exactly one field initializer")
|
||||
return invalid_hir_expr(checker, expr.span, id, struct_type)
|
||||
}
|
||||
values := make([]hir.Expr_Id, 1 if union_record else len(fields), checker.allocator)
|
||||
initialized := make([]bool, len(fields), checker.allocator)
|
||||
defer delete(initialized, checker.allocator)
|
||||
for &value in values {
|
||||
@@ -1803,18 +2043,35 @@ build_compound_expr :: proc(
|
||||
continue
|
||||
}
|
||||
initialized[index] = true
|
||||
values[index] = build_nested_expr(checker, keyed_expr.left, locals, global_reads, calls, field.type, pkg, file)
|
||||
values[index] = coerce_expr(checker, values[index], field.type, keyed_expr.span)
|
||||
value_index := 0 if union_record else index
|
||||
values[value_index] = build_nested_expr(checker, keyed_expr.left, locals, global_reads, calls, field.type, pkg, file)
|
||||
values[value_index] = coerce_expr(checker, values[value_index], field.type, keyed_expr.span)
|
||||
}
|
||||
for field, index in fields {
|
||||
if values[index] == hir.INVALID_EXPR {
|
||||
if !union_record {
|
||||
for field, index in fields {
|
||||
if values[index] != hir.INVALID_EXPR {
|
||||
continue
|
||||
}
|
||||
id := source.addf(checker.diagnostics, expr.span, "missing initializer for struct field '%s'", symbol_text(checker, symbol.Id(field.name)))
|
||||
delete(values, checker.allocator)
|
||||
return invalid_hir_expr(checker, expr.span, id, struct_type)
|
||||
}
|
||||
}
|
||||
active_field: i64
|
||||
if union_record {
|
||||
if values[0] == hir.INVALID_EXPR {
|
||||
delete(values, checker.allocator)
|
||||
return invalid_hir_expr(checker, expr.span, source.add(checker.diagnostics, expr.span, "union literal requires a known field"), struct_type)
|
||||
}
|
||||
for value, index in initialized {
|
||||
if value {
|
||||
active_field = i64(index)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Struct, span=expr.span, type=struct_type, args=values,
|
||||
kind=.Struct, span=expr.span, type=struct_type, args=values, integer=active_field,
|
||||
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
@@ -1938,11 +2195,16 @@ build_expr :: proc(
|
||||
target=hir.global_ref(hir_global), left = hir.INVALID_EXPR, right = hir.INVALID_EXPR, diagnostic = source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
} else {
|
||||
id := add_unsupported_diagnostic(checker, expr.span, target_pkg, expr.name)
|
||||
if id == source.INVALID_DIAGNOSTIC {
|
||||
id = add_name_resolution_diagnostic(checker, expr, target_pkg)
|
||||
template := find_template(checker, expr.name, target_pkg)
|
||||
if template != ast.INVALID_FUNCTION && checker.ast_module.functions[template].c_abi {
|
||||
last = build_function_value(checker, template, expr.span, frame.expected)
|
||||
} else {
|
||||
id := add_unsupported_diagnostic(checker, expr.span, target_pkg, expr.name)
|
||||
if id == source.INVALID_DIAGNOSTIC {
|
||||
id = add_name_resolution_diagnostic(checker, expr, target_pkg)
|
||||
}
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
}
|
||||
_ = pop(&stack)
|
||||
@@ -1953,6 +2215,11 @@ build_expr :: proc(
|
||||
stack[frame_index].stage = 1
|
||||
append(&stack, Build_Expr_Frame{expr=expr.left, expected=types.INVALID, template=ast.INVALID_FUNCTION})
|
||||
case .Call:
|
||||
if expr.left != ast.INVALID_EXPR {
|
||||
stack[frame_index].stage = 6
|
||||
append(&stack, Build_Expr_Frame{expr=expr.left, expected=types.INVALID, template=ast.INVALID_FUNCTION})
|
||||
continue
|
||||
}
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file, true)
|
||||
if !available {
|
||||
id := add_package_resolution_diagnostic(checker, expr, file)
|
||||
@@ -1962,12 +2229,63 @@ build_expr :: proc(
|
||||
}
|
||||
template := find_template(checker, expr.name, target_pkg)
|
||||
if template == ast.INVALID_FUNCTION {
|
||||
id := add_unsupported_diagnostic(checker, expr.span, target_pkg, expr.name)
|
||||
if id == source.INVALID_DIAGNOSTIC {
|
||||
id = add_call_resolution_diagnostic(checker, expr, target_pkg)
|
||||
callee := hir.INVALID_EXPR
|
||||
callee_from_global := false
|
||||
if !symbol.is_valid(expr.qualifier) {
|
||||
if local, ok := find_build_local(locals, expr.name); ok {
|
||||
callee = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Local, span=expr.span, type=local.type, target=hir.local_ref(local.id),
|
||||
left=hir.INVALID_EXPR, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
}
|
||||
if callee == hir.INVALID_EXPR {
|
||||
if global := find_global(checker, expr.name, target_pkg); global != ast.INVALID_GLOBAL {
|
||||
hir_global := hir.Global_Id(global)
|
||||
add_unique_global(global_reads, hir_global)
|
||||
callee = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Global, span=expr.span, type=checker.global_types[global],
|
||||
target=hir.global_ref(hir_global), left=hir.INVALID_EXPR,
|
||||
right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
callee_from_global = true
|
||||
}
|
||||
}
|
||||
if callee == hir.INVALID_EXPR {
|
||||
id := add_unsupported_diagnostic(checker, expr.span, target_pkg, expr.name)
|
||||
if id == source.INVALID_DIAGNOSTIC {
|
||||
id = add_call_resolution_diagnostic(checker, expr, target_pkg)
|
||||
}
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
_, function_item, function_type, ok := types.function_pointer(checker.module.exprs[callee].type, &checker.module.types)
|
||||
if !ok {
|
||||
id := add_call_resolution_diagnostic(checker, expr, target_pkg) if callee_from_global else
|
||||
source.add(checker.diagnostics, expr.span, "call target is not a function pointer")
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
if !valid_callable_arity(function_item, len(expr.args)) {
|
||||
message := "function pointer expects at least %d arguments, got %d" if function_item.variadic else
|
||||
"function pointer expects %d arguments, got %d"
|
||||
id := source.addf(checker.diagnostics, expr.span, message, function_item.field_count, len(expr.args))
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
stack[frame_index].left = callee
|
||||
stack[frame_index].built_args = make([]hir.Expr_Id, len(expr.args), checker.allocator)
|
||||
stack[frame_index].stage = 7
|
||||
if len(expr.args) > 0 {
|
||||
arg_expected := callable_arg_expected(function_type, function_item, &checker.module.types, 0)
|
||||
if !is_runtime_type(checker, arg_expected) {
|
||||
arg_expected = types.INVALID
|
||||
}
|
||||
append(&stack, Build_Expr_Frame{expr=expr.args[0], expected=arg_expected, template=ast.INVALID_FUNCTION})
|
||||
}
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
if len(checker.ast_module.functions[template].unsupported_reason) > 0 {
|
||||
@@ -2143,6 +2461,95 @@ build_expr :: proc(
|
||||
}
|
||||
_ = pop(&stack)
|
||||
}
|
||||
if frame.stage == 6 {
|
||||
callee := last
|
||||
_, function_item, function_type, ok := types.function_pointer(checker.module.exprs[callee].type, &checker.module.types)
|
||||
if !ok {
|
||||
id := source.add(checker.diagnostics, expr.span, "call target is not a function pointer")
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
if !valid_callable_arity(function_item, len(expr.args)) {
|
||||
message := "function pointer expects at least %d arguments, got %d" if function_item.variadic else
|
||||
"function pointer expects %d arguments, got %d"
|
||||
id := source.addf(checker.diagnostics, expr.span, message, function_item.field_count, len(expr.args))
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
stack[frame_index].left = callee
|
||||
stack[frame_index].built_args = make([]hir.Expr_Id, len(expr.args), checker.allocator)
|
||||
stack[frame_index].stage = 7
|
||||
if len(expr.args) > 0 {
|
||||
arg_expected := callable_arg_expected(function_type, function_item, &checker.module.types, 0)
|
||||
if !is_runtime_type(checker, arg_expected) {
|
||||
arg_expected = types.INVALID
|
||||
}
|
||||
append(&stack, Build_Expr_Frame{expr=expr.args[0], expected=arg_expected, template=ast.INVALID_FUNCTION})
|
||||
}
|
||||
continue
|
||||
}
|
||||
if frame.stage == 7 {
|
||||
if frame.arg_index < len(expr.args) {
|
||||
stack[frame_index].built_args[frame.arg_index] = last
|
||||
stack[frame_index].arg_index += 1
|
||||
if frame.arg_index+1 < len(expr.args) {
|
||||
next := frame.arg_index+1
|
||||
callee_type := checker.module.exprs[frame.left].type
|
||||
_, function_item, function_type, _ := types.function_pointer(callee_type, &checker.module.types)
|
||||
arg_expected := callable_arg_expected(function_type, function_item, &checker.module.types, next)
|
||||
if !is_runtime_type(checker, arg_expected) {
|
||||
arg_expected = types.INVALID
|
||||
}
|
||||
append(&stack, Build_Expr_Frame{expr=expr.args[next], expected=arg_expected, template=ast.INVALID_FUNCTION})
|
||||
continue
|
||||
}
|
||||
}
|
||||
callee_type := checker.module.exprs[frame.left].type
|
||||
_, function_item, function_type, ok := types.function_pointer(callee_type, &checker.module.types)
|
||||
if !ok {
|
||||
id := source.add(checker.diagnostics, expr.span, "call target is not a function pointer")
|
||||
delete(stack[frame_index].built_args, checker.allocator)
|
||||
stack[frame_index].built_args = nil
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
fixed_count := int(function_item.field_count)
|
||||
for index in 0..<min(fixed_count, len(stack[frame_index].built_args)) {
|
||||
expected_arg := callable_arg_expected(function_type, function_item, &checker.module.types, index)
|
||||
stack[frame_index].built_args[index] = coerce_expr(
|
||||
checker,
|
||||
stack[frame_index].built_args[index],
|
||||
expected_arg,
|
||||
checker.module.exprs[stack[frame_index].built_args[index]].span,
|
||||
)
|
||||
}
|
||||
for index in fixed_count..<len(stack[frame_index].built_args) {
|
||||
arg := stack[frame_index].built_args[index]
|
||||
stack[frame_index].built_args[index] = promote_c_vararg_expr(
|
||||
checker,
|
||||
arg,
|
||||
checker.module.exprs[arg].span,
|
||||
)
|
||||
}
|
||||
result := function_item.child
|
||||
if !types.is_valid(result) {
|
||||
id := source.add(checker.diagnostics, expr.span, "could not resolve function pointer result type")
|
||||
delete(stack[frame_index].built_args, checker.allocator)
|
||||
stack[frame_index].built_args = nil
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
} else {
|
||||
last = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Call, span=expr.span, type=result, target=hir.INVALID_REF,
|
||||
left=frame.left, right=hir.INVALID_EXPR, args=stack[frame_index].built_args,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
stack[frame_index].built_args = nil
|
||||
}
|
||||
_ = pop(&stack)
|
||||
}
|
||||
}
|
||||
return last
|
||||
}
|
||||
|
||||
@@ -24,19 +24,41 @@ Type_Kind :: enum u8 {
|
||||
C_Double,
|
||||
C_Longdouble,
|
||||
Pointer,
|
||||
Array,
|
||||
Function,
|
||||
Record,
|
||||
}
|
||||
|
||||
Type :: struct {
|
||||
kind: Type_Kind,
|
||||
child: Type_Id,
|
||||
params: []Type_Id,
|
||||
record: u32,
|
||||
count: u64,
|
||||
mutable: bool,
|
||||
variadic: bool,
|
||||
}
|
||||
|
||||
Record_Kind :: enum u8 {
|
||||
Struct,
|
||||
Union,
|
||||
}
|
||||
|
||||
Field :: struct {
|
||||
name: string,
|
||||
type: Type_Id,
|
||||
offset: u64,
|
||||
}
|
||||
|
||||
Record :: struct {
|
||||
name: string,
|
||||
identity: string,
|
||||
fields: [dynamic]Field,
|
||||
size: u64,
|
||||
alignment: u32,
|
||||
kind: Record_Kind,
|
||||
complete: bool,
|
||||
reason: string,
|
||||
}
|
||||
|
||||
Alias :: struct {
|
||||
@@ -82,9 +104,17 @@ init_result :: proc(allocator := context.allocator) -> Result {
|
||||
}
|
||||
|
||||
destroy_result :: proc(result: ^Result) {
|
||||
for type_item in result.types {
|
||||
delete(type_item.params, result.allocator)
|
||||
}
|
||||
for record in result.records {
|
||||
delete(record.name, result.allocator)
|
||||
delete(record.identity, result.allocator)
|
||||
for field in record.fields {
|
||||
delete(field.name, result.allocator)
|
||||
}
|
||||
delete(record.fields)
|
||||
delete(record.reason, result.allocator)
|
||||
}
|
||||
for alias in result.aliases {
|
||||
delete(alias.name, result.allocator)
|
||||
|
||||
@@ -43,16 +43,25 @@ Api :: struct {
|
||||
get_cursor_spelling: proc "c"(CXCursor) -> CXString,
|
||||
get_cursor_usr: proc "c"(CXCursor) -> CXString,
|
||||
get_cursor_linkage: proc "c"(CXCursor) -> i32,
|
||||
get_cursor_definition: proc "c"(CXCursor) -> CXCursor,
|
||||
is_cursor_definition: proc "c"(CXCursor) -> u32,
|
||||
get_cursor_type: proc "c"(CXCursor) -> CXType,
|
||||
get_typedef_underlying_type: proc "c"(CXCursor) -> CXType,
|
||||
get_type_declaration: proc "c"(CXType) -> CXCursor,
|
||||
get_canonical_type: proc "c"(CXType) -> CXType,
|
||||
get_pointee_type: proc "c"(CXType) -> CXType,
|
||||
get_array_element_type: proc "c"(CXType) -> CXType,
|
||||
get_array_size: proc "c"(CXType) -> i64,
|
||||
get_result_type: proc "c"(CXType) -> CXType,
|
||||
get_num_arg_types: proc "c"(CXType) -> i32,
|
||||
get_arg_type: proc "c"(CXType, u32) -> CXType,
|
||||
is_function_type_variadic: proc "c"(CXType) -> u32,
|
||||
is_const_qualified_type: proc "c"(CXType) -> u32,
|
||||
is_volatile_qualified_type: proc "c"(CXType) -> u32,
|
||||
cursor_is_bitfield: proc "c"(CXCursor) -> u32,
|
||||
cursor_get_offset_of_field: proc "c"(CXCursor) -> i64,
|
||||
type_get_size_of: proc "c"(CXType) -> i64,
|
||||
type_get_align_of: proc "c"(CXType) -> i64,
|
||||
cursor_is_variadic: proc "c"(CXCursor) -> u32,
|
||||
get_num_diagnostics: proc "c"(CXTranslationUnit) -> u32,
|
||||
get_diagnostic: proc "c"(CXTranslationUnit, u32) -> CXDiagnostic,
|
||||
@@ -70,6 +79,7 @@ CXCursor_FunctionDecl :: i32(8)
|
||||
CXCursor_VarDecl :: i32(9)
|
||||
CXCursor_TypedefDecl :: i32(20)
|
||||
CXCursor_MacroDefinition :: i32(501)
|
||||
CXCursor_FieldDecl :: i32(6)
|
||||
|
||||
CXLinkage_External :: i32(4)
|
||||
|
||||
@@ -97,6 +107,10 @@ CXType_Enum :: i32(106)
|
||||
CXType_Typedef :: i32(107)
|
||||
CXType_FunctionNoProto :: i32(110)
|
||||
CXType_FunctionProto :: i32(111)
|
||||
CXType_ConstantArray :: i32(112)
|
||||
CXType_IncompleteArray :: i32(114)
|
||||
CXType_VariableArray :: i32(115)
|
||||
CXType_DependentSizedArray :: i32(116)
|
||||
CXType_Elaborated :: i32(119)
|
||||
CXType_Attributed :: i32(163)
|
||||
|
||||
@@ -135,16 +149,25 @@ load_api_from :: proc(path: string) -> (Api, bool) {
|
||||
load_proc(&api, "clang_getCursorSpelling", &api.get_cursor_spelling) &&
|
||||
load_proc(&api, "clang_getCursorUSR", &api.get_cursor_usr) &&
|
||||
load_proc(&api, "clang_getCursorLinkage", &api.get_cursor_linkage) &&
|
||||
load_proc(&api, "clang_getCursorDefinition", &api.get_cursor_definition) &&
|
||||
load_proc(&api, "clang_isCursorDefinition", &api.is_cursor_definition) &&
|
||||
load_proc(&api, "clang_getCursorType", &api.get_cursor_type) &&
|
||||
load_proc(&api, "clang_getTypedefDeclUnderlyingType", &api.get_typedef_underlying_type) &&
|
||||
load_proc(&api, "clang_getTypeDeclaration", &api.get_type_declaration) &&
|
||||
load_proc(&api, "clang_getCanonicalType", &api.get_canonical_type) &&
|
||||
load_proc(&api, "clang_getPointeeType", &api.get_pointee_type) &&
|
||||
load_proc(&api, "clang_getArrayElementType", &api.get_array_element_type) &&
|
||||
load_proc(&api, "clang_getArraySize", &api.get_array_size) &&
|
||||
load_proc(&api, "clang_getResultType", &api.get_result_type) &&
|
||||
load_proc(&api, "clang_getNumArgTypes", &api.get_num_arg_types) &&
|
||||
load_proc(&api, "clang_getArgType", &api.get_arg_type) &&
|
||||
load_proc(&api, "clang_isFunctionTypeVariadic", &api.is_function_type_variadic) &&
|
||||
load_proc(&api, "clang_isConstQualifiedType", &api.is_const_qualified_type) &&
|
||||
load_proc(&api, "clang_isVolatileQualifiedType", &api.is_volatile_qualified_type) &&
|
||||
load_proc(&api, "clang_Cursor_isBitField", &api.cursor_is_bitfield) &&
|
||||
load_proc(&api, "clang_Cursor_getOffsetOfField", &api.cursor_get_offset_of_field) &&
|
||||
load_proc(&api, "clang_Type_getSizeOf", &api.type_get_size_of) &&
|
||||
load_proc(&api, "clang_Type_getAlignOf", &api.type_get_align_of) &&
|
||||
load_proc(&api, "clang_Cursor_isVariadic", &api.cursor_is_variadic) &&
|
||||
load_proc(&api, "clang_getNumDiagnostics", &api.get_num_diagnostics) &&
|
||||
load_proc(&api, "clang_getDiagnostic", &api.get_diagnostic) &&
|
||||
@@ -228,10 +251,99 @@ add_record :: proc(ctx: ^Context, declaration: CXCursor, preferred_name: string)
|
||||
name = clone_cx_string(ctx.api, ctx.api.get_cursor_spelling(declaration), ctx.allocator)
|
||||
}
|
||||
index := u32(len(ctx.result.records))
|
||||
append(&ctx.result.records, Record{name=name, identity=identity})
|
||||
append(&ctx.result.records, Record{
|
||||
name=name,
|
||||
identity=identity,
|
||||
kind=.Union if ctx.api.get_cursor_kind(declaration) == CXCursor_UnionDecl else .Struct,
|
||||
reason=fmt.aprintf("", allocator=ctx.allocator),
|
||||
})
|
||||
ctx.result.records[index].fields.allocator = ctx.allocator
|
||||
return index
|
||||
}
|
||||
|
||||
Record_Field_Context :: struct {
|
||||
ctx: ^Context,
|
||||
record: u32,
|
||||
}
|
||||
|
||||
visit_record_field :: proc "c"(cursor, parent: CXCursor, client_data: rawptr) -> i32 {
|
||||
context = runtime.default_context()
|
||||
field_ctx := (^Record_Field_Context)(client_data)
|
||||
ctx := field_ctx.ctx
|
||||
if ctx.api.get_cursor_kind(cursor) != CXCursor_FieldDecl {
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
if len(ctx.result.records[field_ctx.record].reason) > 0 {
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
name := clone_cx_string(ctx.api, ctx.api.get_cursor_spelling(cursor), ctx.allocator)
|
||||
if len(name) == 0 {
|
||||
delete(name, ctx.allocator)
|
||||
delete(ctx.result.records[field_ctx.record].reason, ctx.allocator)
|
||||
ctx.result.records[field_ctx.record].reason = fmt.aprintf("anonymous C record fields are not supported", allocator=ctx.allocator)
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
field_type := ctx.api.get_cursor_type(cursor)
|
||||
if ctx.api.cursor_is_bitfield(cursor) != 0 {
|
||||
delete(name, ctx.allocator)
|
||||
delete(ctx.result.records[field_ctx.record].reason, ctx.allocator)
|
||||
ctx.result.records[field_ctx.record].reason = fmt.aprintf("C bitfields are not supported", allocator=ctx.allocator)
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
if ctx.api.is_const_qualified_type(field_type) != 0 ||
|
||||
ctx.api.is_volatile_qualified_type(field_type) != 0 {
|
||||
delete(name, ctx.allocator)
|
||||
delete(ctx.result.records[field_ctx.record].reason, ctx.allocator)
|
||||
ctx.result.records[field_ctx.record].reason = fmt.aprintf("qualified C record fields are not supported", allocator=ctx.allocator)
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
if field_type.kind == CXType_IncompleteArray ||
|
||||
field_type.kind == CXType_VariableArray ||
|
||||
field_type.kind == CXType_DependentSizedArray {
|
||||
delete(name, ctx.allocator)
|
||||
delete(ctx.result.records[field_ctx.record].reason, ctx.allocator)
|
||||
ctx.result.records[field_ctx.record].reason = fmt.aprintf("flexible or variable C array fields are not supported", allocator=ctx.allocator)
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
translated := translate_type(ctx, field_type)
|
||||
offset_bits := ctx.api.cursor_get_offset_of_field(cursor)
|
||||
if translated == INVALID_TYPE || offset_bits < 0 || offset_bits%8 != 0 {
|
||||
delete(name, ctx.allocator)
|
||||
delete(ctx.result.records[field_ctx.record].reason, ctx.allocator)
|
||||
ctx.result.records[field_ctx.record].reason = fmt.aprintf("C record field type or layout is not supported", allocator=ctx.allocator)
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
append(&ctx.result.records[field_ctx.record].fields, Field{name=name, type=translated, offset=u64(offset_bits/8)})
|
||||
return CXChildVisit_Continue
|
||||
}
|
||||
|
||||
populate_record :: proc(ctx: ^Context, index: u32, declaration: CXCursor) {
|
||||
if ctx.result.records[index].complete || len(ctx.result.records[index].reason) > 0 {
|
||||
return
|
||||
}
|
||||
definition := declaration
|
||||
if ctx.api.is_cursor_definition(definition) == 0 {
|
||||
definition = ctx.api.get_cursor_definition(declaration)
|
||||
}
|
||||
if ctx.api.is_cursor_definition(definition) == 0 {
|
||||
return
|
||||
}
|
||||
record_type := ctx.api.get_cursor_type(definition)
|
||||
size := ctx.api.type_get_size_of(record_type)
|
||||
alignment := ctx.api.type_get_align_of(record_type)
|
||||
if size < 0 || alignment <= 0 {
|
||||
delete(ctx.result.records[index].reason, ctx.allocator)
|
||||
ctx.result.records[index].reason = fmt.aprintf("C record size or alignment is not supported", allocator=ctx.allocator)
|
||||
return
|
||||
}
|
||||
ctx.result.records[index].kind = .Union if ctx.api.get_cursor_kind(definition) == CXCursor_UnionDecl else .Struct
|
||||
ctx.result.records[index].size = u64(size)
|
||||
ctx.result.records[index].alignment = u32(alignment)
|
||||
field_ctx := Record_Field_Context{ctx=ctx, record=index}
|
||||
_ = ctx.api.visit_children(definition, visit_record_field, &field_ctx)
|
||||
ctx.result.records[index].complete = len(ctx.result.records[index].reason) == 0
|
||||
}
|
||||
|
||||
translate_type :: proc(ctx: ^Context, value: CXType, preferred_record_name := "", depth := 0) -> Type_Id {
|
||||
if depth > 64 || value.kind == CXType_Invalid || ctx.api.is_volatile_qualified_type(value) != 0 {
|
||||
return INVALID_TYPE
|
||||
@@ -259,18 +371,52 @@ translate_type :: proc(ctx: ^Context, value: CXType, preferred_record_name := ""
|
||||
if child == INVALID_TYPE {
|
||||
return INVALID_TYPE
|
||||
}
|
||||
mutable := ctx.api.is_const_qualified_type(pointee) == 0
|
||||
if pointee.kind == CXType_FunctionProto {
|
||||
mutable = false
|
||||
}
|
||||
return add_type(ctx, Type{
|
||||
kind=.Pointer,
|
||||
child=child,
|
||||
mutable=ctx.api.is_const_qualified_type(pointee) == 0,
|
||||
mutable=mutable,
|
||||
})
|
||||
case CXType_Record:
|
||||
declaration := ctx.api.get_type_declaration(value)
|
||||
if ctx.api.get_cursor_kind(declaration) == CXCursor_UnionDecl {
|
||||
case CXType_ConstantArray:
|
||||
count := ctx.api.get_array_size(value)
|
||||
child := translate_type(ctx, ctx.api.get_array_element_type(value), "", depth+1)
|
||||
if count <= 0 || child == INVALID_TYPE {
|
||||
return INVALID_TYPE
|
||||
}
|
||||
return add_type(ctx, Type{kind=.Array, child=child, count=u64(count), mutable=true})
|
||||
case CXType_Record:
|
||||
declaration := ctx.api.get_type_declaration(value)
|
||||
record := add_record(ctx, declaration, preferred_record_name)
|
||||
populate_record(ctx, record, declaration)
|
||||
return add_type(ctx, Type{kind=.Record, child=INVALID_TYPE, record=record})
|
||||
case CXType_FunctionProto:
|
||||
result := translate_type(ctx, ctx.api.get_result_type(value), "", depth+1)
|
||||
if result == INVALID_TYPE {
|
||||
return INVALID_TYPE
|
||||
}
|
||||
count := ctx.api.get_num_arg_types(value)
|
||||
if count < 0 {
|
||||
return INVALID_TYPE
|
||||
}
|
||||
params: [dynamic]Type_Id
|
||||
params.allocator = ctx.allocator
|
||||
for index in 0..<count {
|
||||
param := translate_type(ctx, ctx.api.get_arg_type(value, u32(index)), "", depth+1)
|
||||
append(¶ms, param)
|
||||
if param == INVALID_TYPE {
|
||||
delete(params)
|
||||
return INVALID_TYPE
|
||||
}
|
||||
}
|
||||
return add_type(ctx, Type{
|
||||
kind=.Function,
|
||||
child=result,
|
||||
params=params[:],
|
||||
variadic=ctx.api.is_function_type_variadic(value) != 0,
|
||||
})
|
||||
case CXType_Typedef:
|
||||
declaration := ctx.api.get_type_declaration(value)
|
||||
name := clone_cx_string(ctx.api, ctx.api.get_cursor_spelling(declaration), ctx.allocator)
|
||||
@@ -282,7 +428,8 @@ translate_type :: proc(ctx: ^Context, value: CXType, preferred_record_name := ""
|
||||
return INVALID_TYPE
|
||||
}
|
||||
return translate_type(ctx, canonical, preferred_record_name, depth+1)
|
||||
case CXType_Enum, CXType_FunctionNoProto, CXType_FunctionProto:
|
||||
case CXType_Enum, CXType_FunctionNoProto,
|
||||
CXType_IncompleteArray, CXType_VariableArray, CXType_DependentSizedArray:
|
||||
return INVALID_TYPE
|
||||
}
|
||||
return INVALID_TYPE
|
||||
@@ -373,14 +520,13 @@ visit_cursor :: proc "c"(cursor, parent: CXCursor, client_data: rawptr) -> i32 {
|
||||
reason = "typedef underlying type is not supported"
|
||||
}
|
||||
add_alias(ctx, name, value, reason)
|
||||
case CXCursor_StructDecl:
|
||||
case CXCursor_StructDecl, CXCursor_UnionDecl:
|
||||
if len(name) > 0 {
|
||||
record := add_record(ctx, cursor, name)
|
||||
populate_record(ctx, record, cursor)
|
||||
value := add_type(ctx, Type{kind=.Record, child=INVALID_TYPE, record=record})
|
||||
add_alias(ctx, name, value)
|
||||
}
|
||||
case CXCursor_UnionDecl:
|
||||
add_unsupported(ctx, name, "C unions are not supported")
|
||||
case CXCursor_EnumDecl:
|
||||
add_unsupported(ctx, name, "C enums are not supported")
|
||||
case CXCursor_VarDecl:
|
||||
|
||||
@@ -83,6 +83,7 @@ Expr_Kind :: enum u8 {
|
||||
Optional_Some,
|
||||
Local,
|
||||
Global,
|
||||
Function,
|
||||
Address,
|
||||
Deref,
|
||||
Index,
|
||||
|
||||
@@ -73,6 +73,7 @@ Opcode :: enum u8 {
|
||||
None,
|
||||
Optional_Some,
|
||||
Load_Global,
|
||||
Function_Address,
|
||||
Address_Global,
|
||||
Address_Of,
|
||||
Alloca,
|
||||
|
||||
+494
-30
@@ -22,6 +22,118 @@ Emitter :: struct {
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
|
||||
C_Record_ABI_Kind :: enum u8 {
|
||||
None,
|
||||
Small_Integer,
|
||||
Integer_Pair,
|
||||
Homogeneous_Float,
|
||||
Indirect,
|
||||
}
|
||||
|
||||
C_Record_ABI :: struct {
|
||||
kind: C_Record_ABI_Kind,
|
||||
size: u64,
|
||||
alignment: int,
|
||||
float_type: types.Type,
|
||||
float_count: int,
|
||||
}
|
||||
|
||||
hfa_walk :: proc(value: types.Type, store: ^types.Store, scalar: ^types.Type, count: ^int, depth := 0) -> bool {
|
||||
if depth > 64 || count^ > 4 {
|
||||
return false
|
||||
}
|
||||
if types.is_float(value, store.selected) {
|
||||
repr := types.representation(value, store.selected)
|
||||
if scalar^ == types.INVALID {
|
||||
scalar^ = repr
|
||||
}
|
||||
if scalar^ != repr {
|
||||
return false
|
||||
}
|
||||
count^ += 1
|
||||
return count^ <= 4
|
||||
}
|
||||
item, ok := types.node(store, value)
|
||||
if !ok || item.kind == .Union {
|
||||
return false
|
||||
}
|
||||
if item.kind == .Array {
|
||||
for _ in 0..<int(item.count) {
|
||||
if !hfa_walk(item.child, store, scalar, count, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
if item.kind != .Struct {
|
||||
return false
|
||||
}
|
||||
for field in types.fields_for(store, value) {
|
||||
if !hfa_walk(field.type, store, scalar, count, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
c_record_abi :: proc(value: types.Type, store: ^types.Store) -> C_Record_ABI {
|
||||
if !types.is_record(value, store) {
|
||||
return {}
|
||||
}
|
||||
result := C_Record_ABI{
|
||||
size=types.size(value, store, store.selected),
|
||||
alignment=types.alignment_of(value, store, store.selected),
|
||||
}
|
||||
scalar := types.INVALID
|
||||
count := 0
|
||||
if !types.is_union(value, store) && hfa_walk(value, store, &scalar, &count) && count > 0 {
|
||||
result.kind = .Homogeneous_Float
|
||||
result.float_type = scalar
|
||||
result.float_count = count
|
||||
return result
|
||||
}
|
||||
if result.size <= 8 {
|
||||
result.kind = .Small_Integer
|
||||
} else if result.size <= 16 {
|
||||
result.kind = .Integer_Pair
|
||||
} else {
|
||||
result.kind = .Indirect
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
c_abi_param_type :: proc(value: types.Type, store: ^types.Store) -> string {
|
||||
abi := c_record_abi(value, store)
|
||||
switch abi.kind {
|
||||
case .None:
|
||||
return llvm_type(value, store)
|
||||
case .Small_Integer:
|
||||
return "i64"
|
||||
case .Integer_Pair:
|
||||
return "[2 x i64]"
|
||||
case .Homogeneous_Float:
|
||||
return fmt.tprintf("[%d x %s]", abi.float_count, llvm_type(abi.float_type, store))
|
||||
case .Indirect:
|
||||
return "ptr"
|
||||
}
|
||||
return llvm_type(value, store)
|
||||
}
|
||||
|
||||
c_abi_result_type :: proc(value: types.Type, store: ^types.Store) -> string {
|
||||
abi := c_record_abi(value, store)
|
||||
switch abi.kind {
|
||||
case .None, .Homogeneous_Float:
|
||||
return llvm_type(value, store)
|
||||
case .Small_Integer:
|
||||
return fmt.tprintf("i%d", abi.size*8)
|
||||
case .Integer_Pair:
|
||||
return "[2 x i64]"
|
||||
case .Indirect:
|
||||
return "void"
|
||||
}
|
||||
return llvm_type(value, store)
|
||||
}
|
||||
|
||||
llvm_type :: proc(value: types.Type, store: ^types.Store = nil) -> string {
|
||||
if types.is_void(value) {
|
||||
return "void"
|
||||
@@ -40,7 +152,7 @@ llvm_type :: proc(value: types.Type, store: ^types.Store = nil) -> string {
|
||||
return "ptr"
|
||||
}
|
||||
return fmt.tprintf("{{ i1, %s }}", llvm_type(item.child, store))
|
||||
case .Struct:
|
||||
case .Struct, .Union:
|
||||
return fmt.tprintf("%%bro.type.%d", value)
|
||||
}
|
||||
selected := store.selected if store != nil else target.DEFAULT
|
||||
@@ -60,6 +172,9 @@ function_result_type :: proc(function: ir.Function, store: ^types.Store) -> stri
|
||||
if function.is_main {
|
||||
return "i32"
|
||||
}
|
||||
if function.calling_convention == .C {
|
||||
return c_abi_result_type(function.result, store)
|
||||
}
|
||||
return llvm_type(function.result, store)
|
||||
}
|
||||
|
||||
@@ -111,7 +226,7 @@ valid_value :: proc(
|
||||
}
|
||||
switch instructions[value_id].op {
|
||||
case .Param, .Const, .String, .Aggregate, .None, .Optional_Some,
|
||||
.Load_Global, .Address_Of, .Load, .Slice, .Length, .Slice_Ptr, .Unwrap, .Orelse,
|
||||
.Load_Global, .Function_Address, .Address_Of, .Load, .Slice, .Length, .Slice_Ptr, .Unwrap, .Orelse,
|
||||
.Widen, .C_Vararg_Promote, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer,
|
||||
.Neg_Checked, .Add_Checked, .Pointer_Add, .Call:
|
||||
return true
|
||||
@@ -289,11 +404,70 @@ emit_call_args :: proc(
|
||||
}
|
||||
}
|
||||
|
||||
emit_pack_c_record_arg :: proc(
|
||||
emitter: ^Emitter,
|
||||
instructions: []ir.Instruction,
|
||||
value: ir.Instruction_Id,
|
||||
value_type: types.Type,
|
||||
call_index, arg_index: int,
|
||||
) -> string {
|
||||
abi := c_record_abi(value_type, &emitter.module.types)
|
||||
if abi.kind == .None {
|
||||
return fmt.tprintf("%%v%d", value)
|
||||
}
|
||||
if abi.kind == .Indirect {
|
||||
fmt.sbprintf(&emitter.builder, " %%abi_arg_slot%d_%d = alloca %s, align %d\n", call_index, arg_index, llvm_type(value_type, &emitter.module.types), abi.alignment)
|
||||
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(value_type, &emitter.module.types))
|
||||
write_operand(&emitter.builder, instructions, value, value_type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", ptr %%abi_arg_slot%d_%d\n", call_index, arg_index)
|
||||
return fmt.tprintf("%%abi_arg_slot%d_%d", call_index, arg_index)
|
||||
}
|
||||
abi_type := c_abi_param_type(value_type, &emitter.module.types)
|
||||
temp_alignment := max(abi.alignment, 8)
|
||||
fmt.sbprintf(&emitter.builder, " %%abi_arg_value_slot%d_%d = alloca %s, align %d\n", call_index, arg_index, llvm_type(value_type, &emitter.module.types), abi.alignment)
|
||||
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(value_type, &emitter.module.types))
|
||||
write_operand(&emitter.builder, instructions, value, value_type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", ptr %%abi_arg_value_slot%d_%d\n", call_index, arg_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%abi_arg_slot%d_%d = alloca %s, align %d\n", call_index, arg_index, abi_type, temp_alignment)
|
||||
fmt.sbprintf(&emitter.builder, " store %s zeroinitializer, ptr %%abi_arg_slot%d_%d\n", abi_type, call_index, arg_index)
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" call void @llvm.memcpy.p0.p0.i64(ptr align %d %%abi_arg_slot%d_%d, ptr align %d %%abi_arg_value_slot%d_%d, i64 %d, i1 false)\n",
|
||||
temp_alignment, call_index, arg_index, abi.alignment, call_index, arg_index, abi.size,
|
||||
)
|
||||
fmt.sbprintf(&emitter.builder, " %%abi_arg%d_%d = load %s, ptr %%abi_arg_slot%d_%d\n", call_index, arg_index, abi_type, call_index, arg_index)
|
||||
return fmt.tprintf("%%abi_arg%d_%d", call_index, arg_index)
|
||||
}
|
||||
|
||||
emit_unpack_c_record :: proc(
|
||||
emitter: ^Emitter,
|
||||
value_type: types.Type,
|
||||
abi_type, abi_name, result_name: string,
|
||||
tag: int,
|
||||
) {
|
||||
abi := c_record_abi(value_type, &emitter.module.types)
|
||||
if abi.kind == .Indirect {
|
||||
fmt.sbprintf(&emitter.builder, " %s = load %s, ptr %s\n", result_name, llvm_type(value_type, &emitter.module.types), abi_name)
|
||||
return
|
||||
}
|
||||
temp_alignment := max(abi.alignment, 8)
|
||||
fmt.sbprintf(&emitter.builder, " %%abi_unpack_source_slot%d = alloca %s, align %d\n", tag, abi_type, temp_alignment)
|
||||
fmt.sbprintf(&emitter.builder, " store %s %s, ptr %%abi_unpack_source_slot%d\n", abi_type, abi_name, tag)
|
||||
fmt.sbprintf(&emitter.builder, " %%abi_unpack_slot%d = alloca %s, align %d\n", tag, llvm_type(value_type, &emitter.module.types), abi.alignment)
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" call void @llvm.memcpy.p0.p0.i64(ptr align %d %%abi_unpack_slot%d, ptr align %d %%abi_unpack_source_slot%d, i64 %d, i1 false)\n",
|
||||
abi.alignment, tag, temp_alignment, tag, abi.size,
|
||||
)
|
||||
fmt.sbprintf(&emitter.builder, " %s = load %s, ptr %%abi_unpack_slot%d\n", result_name, llvm_type(value_type, &emitter.module.types), tag)
|
||||
}
|
||||
|
||||
emit_instruction_stream :: proc(
|
||||
emitter: ^Emitter,
|
||||
instructions: []ir.Instruction,
|
||||
function: ir.Function,
|
||||
global_initializer := false,
|
||||
sret_name := "",
|
||||
) -> ir.Instruction_Id {
|
||||
return_value := ir.INVALID_INSTRUCTION
|
||||
after_return := false
|
||||
@@ -326,6 +500,8 @@ emit_instruction_stream :: proc(
|
||||
expected_count = int(item.count)
|
||||
} else if ok && item.kind == .Struct {
|
||||
expected_count = int(item.field_count)
|
||||
} else if ok && item.kind == .Union {
|
||||
expected_count = 1
|
||||
} else {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid aggregate type")
|
||||
continue
|
||||
@@ -335,6 +511,22 @@ emit_instruction_stream :: proc(
|
||||
continue
|
||||
}
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
if item.kind == .Union {
|
||||
fields := types.fields_for(&emitter.module.types, instruction.type)
|
||||
field_index := int(instruction.integer)
|
||||
if field_index < 0 || field_index >= len(fields) ||
|
||||
!valid_value(instructions, instruction.args[0], fields[field_index].type, &emitter.module.types) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid union aggregate operands")
|
||||
continue
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " %%union_slot%d = alloca %s, align %d\n", instruction_index, type_name, types.alignment_of(instruction.type, &emitter.module.types, emitter.module.target))
|
||||
fmt.sbprintf(&emitter.builder, " store %s zeroinitializer, ptr %%union_slot%d\n", type_name, instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(fields[field_index].type, &emitter.module.types))
|
||||
write_operand(&emitter.builder, instructions, instruction.args[0], fields[field_index].type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", ptr %%union_slot%d\n", instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%union_slot%d\n", instruction_index, type_name, instruction_index)
|
||||
continue
|
||||
}
|
||||
total := len(instruction.args) + (1 if item.kind == .Array && item.has_sentinel else 0)
|
||||
if total == 0 {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = freeze %s zeroinitializer\n", instruction_index, type_name)
|
||||
@@ -425,6 +617,14 @@ emit_instruction_stream :: proc(
|
||||
global_id,
|
||||
)
|
||||
}
|
||||
case .Function_Address:
|
||||
function_id := ir.as_function(instruction.target)
|
||||
if function_id == ir.INVALID_FUNCTION || int(function_id) >= len(emitter.module.functions) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid function reference")
|
||||
continue
|
||||
}
|
||||
target := emitter.module.functions[function_id]
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, ptr @%s, ptr null\n", instruction_index, target.link_name)
|
||||
case .Address_Global:
|
||||
global_id := ir.as_global(instruction.target)
|
||||
if global_id == ir.INVALID_GLOBAL || int(global_id) >= len(emitter.module.globals) ||
|
||||
@@ -529,11 +729,15 @@ emit_instruction_stream :: proc(
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid field reference")
|
||||
continue
|
||||
}
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" %%v%d = getelementptr %s, ptr %%v%d, i32 0, i32 %d\n",
|
||||
instruction_index, llvm_type(base_type, &emitter.module.types), instruction.a, field_index,
|
||||
)
|
||||
if types.is_union(base_type, &emitter.module.types) {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr i8, ptr %%v%d, i64 0\n", instruction_index, instruction.a)
|
||||
} else {
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" %%v%d = getelementptr %s, ptr %%v%d, i32 0, i32 %d\n",
|
||||
instruction_index, llvm_type(base_type, &emitter.module.types), instruction.a, field_index,
|
||||
)
|
||||
}
|
||||
case .Load:
|
||||
if !valid_address(instructions, instruction.a, instruction.type, &emitter.module.types) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid load slot")
|
||||
@@ -868,7 +1072,138 @@ emit_instruction_stream :: proc(
|
||||
strings.write_string(&emitter.builder, "\n")
|
||||
case .Call:
|
||||
function_id := ir.as_function(instruction.target)
|
||||
if function_id == ir.INVALID_FUNCTION || int(function_id) >= len(emitter.module.functions) {
|
||||
if function_id == ir.INVALID_FUNCTION {
|
||||
if !valid_instruction(instructions, instruction.a) ||
|
||||
!valid_value(instructions, instruction.a, instructions[instruction.a].type, &emitter.module.types) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid function pointer call target")
|
||||
continue
|
||||
}
|
||||
callee := instructions[instruction.a]
|
||||
_, function_item, function_type, ok := types.function_pointer(callee.type, &emitter.module.types)
|
||||
if !ok {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid function pointer call target")
|
||||
continue
|
||||
}
|
||||
param_fields := types.params_for(&emitter.module.types, function_type)
|
||||
valid_args := (len(instruction.args) >= len(param_fields) if function_item.variadic else
|
||||
len(instruction.args) == len(param_fields)) &&
|
||||
(!function_item.variadic || function_item.c_abi)
|
||||
if valid_args {
|
||||
for arg, index in instruction.args {
|
||||
expected := param_fields[index].type if index < len(param_fields) && valid_instruction(instructions, arg) else
|
||||
(instructions[arg].type if valid_instruction(instructions, arg) else types.INVALID)
|
||||
if index >= len(param_fields) &&
|
||||
(!types.is_c_vararg_type(expected, &emitter.module.types) ||
|
||||
!types.equal(types.c_vararg_promotion(expected, emitter.module.target), expected)) {
|
||||
valid_args = false
|
||||
break
|
||||
}
|
||||
if !valid_value(instructions, arg, expected, &emitter.module.types) {
|
||||
valid_args = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !valid_args || !types.equal(instruction.type, function_item.child) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid function pointer call operands")
|
||||
continue
|
||||
}
|
||||
arg_names := make([]string, len(instruction.args), context.temp_allocator)
|
||||
for arg, index in instruction.args {
|
||||
if function_item.c_abi && index < len(param_fields) &&
|
||||
types.is_record(param_fields[index].type, &emitter.module.types) {
|
||||
arg_names[index] = emit_pack_c_record_arg(
|
||||
emitter, instructions, arg, param_fields[index].type, instruction_index, index,
|
||||
)
|
||||
}
|
||||
}
|
||||
result_abi := C_Record_ABI{}
|
||||
if function_item.c_abi {
|
||||
result_abi = c_record_abi(function_item.child, &emitter.module.types)
|
||||
}
|
||||
if result_abi.kind == .Indirect {
|
||||
fmt.sbprintf(&emitter.builder, " %%abi_result_slot%d = alloca %s, align %d\n", instruction_index, llvm_type(function_item.child, &emitter.module.types), result_abi.alignment)
|
||||
strings.write_string(&emitter.builder, " ")
|
||||
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
|
||||
fmt.sbprintf(&emitter.builder, " %%abi_result%d = ", instruction_index)
|
||||
} else if !types.is_void(instruction.type) {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_index)
|
||||
} else {
|
||||
strings.write_string(&emitter.builder, " ")
|
||||
}
|
||||
strings.write_string(&emitter.builder, "call ")
|
||||
if !function_item.c_abi {
|
||||
strings.write_string(&emitter.builder, "fastcc ")
|
||||
}
|
||||
if function_item.c_abi {
|
||||
extension := c_abi_extension(function_item.child, emitter.module.target)
|
||||
if len(extension) > 0 {
|
||||
fmt.sbprintf(&emitter.builder, "%s ", extension)
|
||||
}
|
||||
strings.write_string(&emitter.builder, c_abi_result_type(function_item.child, &emitter.module.types))
|
||||
} else {
|
||||
strings.write_string(&emitter.builder, llvm_type(function_item.child, &emitter.module.types))
|
||||
}
|
||||
if function_item.variadic {
|
||||
strings.write_string(&emitter.builder, " (")
|
||||
wrote_type := false
|
||||
if result_abi.kind == .Indirect {
|
||||
fmt.sbprintf(&emitter.builder, "ptr")
|
||||
wrote_type = true
|
||||
}
|
||||
for param in param_fields {
|
||||
if wrote_type {
|
||||
strings.write_string(&emitter.builder, ", ")
|
||||
}
|
||||
strings.write_string(&emitter.builder, c_abi_param_type(param.type, &emitter.module.types))
|
||||
wrote_type = true
|
||||
}
|
||||
if wrote_type {
|
||||
strings.write_string(&emitter.builder, ", ")
|
||||
}
|
||||
strings.write_string(&emitter.builder, "...)")
|
||||
}
|
||||
strings.write_string(&emitter.builder, " ")
|
||||
write_operand(&emitter.builder, instructions, instruction.a, callee.type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, "(")
|
||||
wrote_arg := false
|
||||
if result_abi.kind == .Indirect {
|
||||
fmt.sbprintf(&emitter.builder, "ptr sret(%s) align %d %%abi_result_slot%d", llvm_type(function_item.child, &emitter.module.types), result_abi.alignment, instruction_index)
|
||||
wrote_arg = true
|
||||
}
|
||||
for arg, index in instruction.args {
|
||||
if wrote_arg {
|
||||
strings.write_string(&emitter.builder, ", ")
|
||||
}
|
||||
arg_type := param_fields[index].type if index < len(param_fields) else instructions[arg].type
|
||||
fixed := index < len(param_fields)
|
||||
if function_item.c_abi && fixed && types.is_record(arg_type, &emitter.module.types) {
|
||||
fmt.sbprintf(&emitter.builder, "%s %s", c_abi_param_type(arg_type, &emitter.module.types), arg_names[index])
|
||||
} else {
|
||||
fmt.sbprintf(&emitter.builder, "%s ", llvm_type(arg_type, &emitter.module.types))
|
||||
if function_item.c_abi && fixed {
|
||||
extension := c_abi_extension(arg_type, emitter.module.target)
|
||||
if len(extension) > 0 {
|
||||
fmt.sbprintf(&emitter.builder, "%s ", extension)
|
||||
}
|
||||
}
|
||||
write_operand(&emitter.builder, instructions, arg, arg_type, &emitter.module.types)
|
||||
}
|
||||
wrote_arg = true
|
||||
}
|
||||
strings.write_string(&emitter.builder, ")\n")
|
||||
if result_abi.kind == .Indirect {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%abi_result_slot%d\n", instruction_index, llvm_type(function_item.child, &emitter.module.types), instruction_index)
|
||||
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
|
||||
emit_unpack_c_record(
|
||||
emitter, function_item.child, c_abi_result_type(function_item.child, &emitter.module.types),
|
||||
fmt.tprintf("%%abi_result%d", instruction_index), fmt.tprintf("%%v%d", instruction_index),
|
||||
100000+instruction_index,
|
||||
)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if int(function_id) >= len(emitter.module.functions) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid function specialization")
|
||||
continue
|
||||
}
|
||||
@@ -900,7 +1235,25 @@ emit_instruction_stream :: proc(
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid function call operands")
|
||||
continue
|
||||
}
|
||||
if !types.is_void(instruction.type) {
|
||||
arg_names := make([]string, len(instruction.args), context.temp_allocator)
|
||||
for arg, index in instruction.args {
|
||||
if target.calling_convention == .C && index < len(target.param_types) &&
|
||||
types.is_record(target.param_types[index], &emitter.module.types) {
|
||||
arg_names[index] = emit_pack_c_record_arg(
|
||||
emitter, instructions, arg, target.param_types[index], instruction_index, index,
|
||||
)
|
||||
}
|
||||
}
|
||||
result_abi := C_Record_ABI{}
|
||||
if target.calling_convention == .C {
|
||||
result_abi = c_record_abi(target.result, &emitter.module.types)
|
||||
}
|
||||
if result_abi.kind == .Indirect {
|
||||
fmt.sbprintf(&emitter.builder, " %%abi_result_slot%d = alloca %s, align %d\n", instruction_index, llvm_type(target.result, &emitter.module.types), result_abi.alignment)
|
||||
strings.write_string(&emitter.builder, " ")
|
||||
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
|
||||
fmt.sbprintf(&emitter.builder, " %%abi_result%d = ", instruction_index)
|
||||
} else if !types.is_void(instruction.type) {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_index)
|
||||
} else {
|
||||
strings.write_string(&emitter.builder, " ")
|
||||
@@ -912,23 +1265,59 @@ emit_instruction_stream :: proc(
|
||||
emit_function_result(&emitter.builder, target, &emitter.module.types)
|
||||
if target.variadic {
|
||||
strings.write_string(&emitter.builder, " (")
|
||||
wrote_type := false
|
||||
if result_abi.kind == .Indirect {
|
||||
fmt.sbprintf(&emitter.builder, "ptr")
|
||||
wrote_type = true
|
||||
}
|
||||
for param_type, index in target.param_types {
|
||||
if index > 0 {
|
||||
if wrote_type || index > 0 {
|
||||
strings.write_string(&emitter.builder, ", ")
|
||||
}
|
||||
strings.write_string(&emitter.builder, llvm_type(param_type, &emitter.module.types))
|
||||
strings.write_string(&emitter.builder, c_abi_param_type(param_type, &emitter.module.types))
|
||||
wrote_type = true
|
||||
}
|
||||
if len(target.param_types) > 0 {
|
||||
if wrote_type {
|
||||
strings.write_string(&emitter.builder, ", ")
|
||||
}
|
||||
strings.write_string(&emitter.builder, "...)")
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " @%s(", target.link_name)
|
||||
emit_call_args(
|
||||
&emitter.builder, instructions, instruction.args, target.param_types,
|
||||
&emitter.module.types, target.calling_convention == .C,
|
||||
)
|
||||
wrote_arg := false
|
||||
if result_abi.kind == .Indirect {
|
||||
fmt.sbprintf(&emitter.builder, "ptr sret(%s) align %d %%abi_result_slot%d", llvm_type(target.result, &emitter.module.types), result_abi.alignment, instruction_index)
|
||||
wrote_arg = true
|
||||
}
|
||||
for arg, index in instruction.args {
|
||||
if wrote_arg {
|
||||
strings.write_string(&emitter.builder, ", ")
|
||||
}
|
||||
arg_type := target.param_types[index] if index < len(target.param_types) else instructions[arg].type
|
||||
fixed := index < len(target.param_types)
|
||||
if target.calling_convention == .C && fixed && types.is_record(arg_type, &emitter.module.types) {
|
||||
fmt.sbprintf(&emitter.builder, "%s %s", c_abi_param_type(arg_type, &emitter.module.types), arg_names[index])
|
||||
} else {
|
||||
fmt.sbprintf(&emitter.builder, "%s ", llvm_type(arg_type, &emitter.module.types))
|
||||
if target.calling_convention == .C && fixed {
|
||||
extension := c_abi_extension(arg_type, emitter.module.target)
|
||||
if len(extension) > 0 {
|
||||
fmt.sbprintf(&emitter.builder, "%s ", extension)
|
||||
}
|
||||
}
|
||||
write_operand(&emitter.builder, instructions, arg, arg_type, &emitter.module.types)
|
||||
}
|
||||
wrote_arg = true
|
||||
}
|
||||
strings.write_string(&emitter.builder, ")\n")
|
||||
if result_abi.kind == .Indirect {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = load %s, ptr %%abi_result_slot%d\n", instruction_index, llvm_type(target.result, &emitter.module.types), instruction_index)
|
||||
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
|
||||
emit_unpack_c_record(
|
||||
emitter, target.result, c_abi_result_type(target.result, &emitter.module.types),
|
||||
fmt.tprintf("%%abi_result%d", instruction_index), fmt.tprintf("%%v%d", instruction_index),
|
||||
100000+instruction_index,
|
||||
)
|
||||
}
|
||||
case .Trap:
|
||||
message := diagnostic_message(emitter, instruction.diagnostic, instruction.span, "invalid recovered source")
|
||||
emit_trap_call(emitter, message)
|
||||
@@ -937,9 +1326,31 @@ emit_instruction_stream :: proc(
|
||||
return_value = instruction.a
|
||||
continue
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " ret %s ", function_result_type(function, &emitter.module.types))
|
||||
write_operand(&emitter.builder, instructions, instruction.a, function.result, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, "\n")
|
||||
result_abi := c_record_abi(function.result, &emitter.module.types) if function.calling_convention == .C else C_Record_ABI{}
|
||||
if result_abi.kind == .Indirect {
|
||||
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(function.result, &emitter.module.types))
|
||||
write_operand(&emitter.builder, instructions, instruction.a, function.result, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", ptr %s\n ret void\n", sret_name)
|
||||
} else if result_abi.kind != .None && result_abi.kind != .Homogeneous_Float {
|
||||
abi_type := c_abi_result_type(function.result, &emitter.module.types)
|
||||
temp_alignment := max(result_abi.alignment, 8)
|
||||
fmt.sbprintf(&emitter.builder, " %%abi_return_value_slot%d = alloca %s, align %d\n", instruction_index, llvm_type(function.result, &emitter.module.types), result_abi.alignment)
|
||||
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(function.result, &emitter.module.types))
|
||||
write_operand(&emitter.builder, instructions, instruction.a, function.result, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", ptr %%abi_return_value_slot%d\n", instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%abi_return_slot%d = alloca %s, align %d\n", instruction_index, abi_type, temp_alignment)
|
||||
fmt.sbprintf(&emitter.builder, " store %s zeroinitializer, ptr %%abi_return_slot%d\n", abi_type, instruction_index)
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" call void @llvm.memcpy.p0.p0.i64(ptr align %d %%abi_return_slot%d, ptr align %d %%abi_return_value_slot%d, i64 %d, i1 false)\n",
|
||||
temp_alignment, instruction_index, result_abi.alignment, instruction_index, result_abi.size,
|
||||
)
|
||||
fmt.sbprintf(&emitter.builder, " %%abi_return%d = load %s, ptr %%abi_return_slot%d\n ret %s %%abi_return%d\n", instruction_index, abi_type, instruction_index, abi_type, instruction_index)
|
||||
} else {
|
||||
fmt.sbprintf(&emitter.builder, " ret %s ", function_result_type(function, &emitter.module.types))
|
||||
write_operand(&emitter.builder, instructions, instruction.a, function.result, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, "\n")
|
||||
}
|
||||
after_return = true
|
||||
case .Return_Void:
|
||||
if global_initializer {
|
||||
@@ -982,7 +1393,7 @@ emit_globals :: proc(emitter: ^Emitter) {
|
||||
|
||||
emit_types :: proc(emitter: ^Emitter) {
|
||||
for item, index in emitter.module.types.nodes {
|
||||
if item.kind != .Struct {
|
||||
if item.kind != .Struct && item.kind != .Union {
|
||||
continue
|
||||
}
|
||||
id := types.DYNAMIC_START+types.Type(index)
|
||||
@@ -991,6 +1402,34 @@ emit_types :: proc(emitter: ^Emitter) {
|
||||
strings.write_string(&emitter.builder, "opaque\n")
|
||||
continue
|
||||
}
|
||||
if item.kind == .Union {
|
||||
fields := types.fields_for(&emitter.module.types, id)
|
||||
carrier := types.INVALID
|
||||
carrier_size: u64
|
||||
carrier_alignment := 0
|
||||
for field in fields {
|
||||
field_alignment := types.alignment_of(field.type, &emitter.module.types, emitter.module.target)
|
||||
field_size := types.size(field.type, &emitter.module.types, emitter.module.target)
|
||||
if field_alignment > carrier_alignment ||
|
||||
(field_alignment == carrier_alignment && field_size > carrier_size) {
|
||||
carrier = field.type
|
||||
carrier_size = field_size
|
||||
carrier_alignment = field_alignment
|
||||
}
|
||||
}
|
||||
total_size := types.size(id, &emitter.module.types, emitter.module.target)
|
||||
if !types.is_valid(carrier) {
|
||||
fmt.sbprintf(&emitter.builder, "[%d x i8]\n", total_size)
|
||||
continue
|
||||
}
|
||||
strings.write_string(&emitter.builder, "{ ")
|
||||
strings.write_string(&emitter.builder, llvm_type(carrier, &emitter.module.types))
|
||||
if carrier_size < total_size {
|
||||
fmt.sbprintf(&emitter.builder, ", [%d x i8]", total_size-carrier_size)
|
||||
}
|
||||
strings.write_string(&emitter.builder, " }\n")
|
||||
continue
|
||||
}
|
||||
strings.write_string(&emitter.builder, "{ ")
|
||||
for field, field_index in types.fields_for(&emitter.module.types, id) {
|
||||
if field_index > 0 {
|
||||
@@ -1108,24 +1547,38 @@ emit_functions :: proc(emitter: ^Emitter) {
|
||||
}
|
||||
emit_function_result(&emitter.builder, function, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, " @%s(", function.link_name)
|
||||
result_abi := c_record_abi(function.result, &emitter.module.types) if function.calling_convention == .C else C_Record_ABI{}
|
||||
wrote_param := false
|
||||
if result_abi.kind == .Indirect {
|
||||
fmt.sbprintf(
|
||||
&emitter.builder, "ptr sret(%s) align %d",
|
||||
llvm_type(function.result, &emitter.module.types), result_abi.alignment,
|
||||
)
|
||||
if function.implementation != .Declaration {
|
||||
strings.write_string(&emitter.builder, " %abi_sret")
|
||||
}
|
||||
wrote_param = true
|
||||
}
|
||||
for param_type, index in function.param_types {
|
||||
if index > 0 {
|
||||
if wrote_param || index > 0 {
|
||||
strings.write_string(&emitter.builder, ", ")
|
||||
}
|
||||
if function.implementation == .Declaration {
|
||||
fmt.sbprintf(&emitter.builder, "%s", llvm_type(param_type, &emitter.module.types))
|
||||
} else {
|
||||
fmt.sbprintf(&emitter.builder, "%s", llvm_type(param_type, &emitter.module.types))
|
||||
}
|
||||
if function.calling_convention == .C {
|
||||
type_name := c_abi_param_type(param_type, &emitter.module.types) if function.calling_convention == .C else llvm_type(param_type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, "%s", type_name)
|
||||
if function.calling_convention == .C && !types.is_record(param_type, &emitter.module.types) {
|
||||
extension := c_abi_extension(param_type, emitter.module.target)
|
||||
if len(extension) > 0 {
|
||||
fmt.sbprintf(&emitter.builder, " %s", extension)
|
||||
}
|
||||
}
|
||||
if function.implementation != .Declaration {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d", index)
|
||||
if function.calling_convention == .C && types.is_record(param_type, &emitter.module.types) {
|
||||
fmt.sbprintf(&emitter.builder, " %%abi_p%d", index)
|
||||
} else {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d", index)
|
||||
}
|
||||
}
|
||||
wrote_param = true
|
||||
}
|
||||
if function.variadic {
|
||||
if len(function.param_types) > 0 {
|
||||
@@ -1138,7 +1591,18 @@ emit_functions :: proc(emitter: ^Emitter) {
|
||||
continue
|
||||
}
|
||||
strings.write_string(&emitter.builder, ") {\nentry:\n")
|
||||
_ = emit_instruction_stream(emitter, function.instructions, function)
|
||||
if function.calling_convention == .C {
|
||||
for param_type, index in function.param_types {
|
||||
if !types.is_record(param_type, &emitter.module.types) {
|
||||
continue
|
||||
}
|
||||
emit_unpack_c_record(
|
||||
emitter, param_type, c_abi_param_type(param_type, &emitter.module.types),
|
||||
fmt.tprintf("%%abi_p%d", index), fmt.tprintf("%%v%d", index), 200000+index,
|
||||
)
|
||||
}
|
||||
}
|
||||
_ = emit_instruction_stream(emitter, function.instructions, function, sret_name="%abi_sret")
|
||||
strings.write_string(&emitter.builder, "}\n\n")
|
||||
}
|
||||
}
|
||||
@@ -1163,7 +1627,7 @@ emit_messages :: proc(emitter: ^Emitter) {
|
||||
}
|
||||
|
||||
emit_declarations :: proc(emitter: ^Emitter) {
|
||||
strings.write_string(&emitter.builder, "declare i64 @write(i32, ptr, i64)\ndeclare void @llvm.trap()\n")
|
||||
strings.write_string(&emitter.builder, "declare i64 @write(i32, ptr, i64)\ndeclare void @llvm.trap()\ndeclare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1 immarg)\n")
|
||||
widths := [?]int{8, 16, 32, 64}
|
||||
for bits in widths {
|
||||
strings.write_string(&emitter.builder, "declare { i")
|
||||
|
||||
+168
-4
@@ -168,6 +168,26 @@ translate_c_type :: proc(
|
||||
pointer := types.pointer(&state.module.type_store, child, item.mutable, true)
|
||||
translated = types.optional(&state.module.type_store, pointer)
|
||||
}
|
||||
case .Array:
|
||||
child := translate_c_type(state, result, item.child, pkg, record_mapping, type_mapping)
|
||||
if types.is_valid(child) {
|
||||
translated = types.array(&state.module.type_store, child, item.count, item.mutable)
|
||||
}
|
||||
case .Function:
|
||||
params := make([]types.Type, len(item.params), state.allocator)
|
||||
for param, index in item.params {
|
||||
params[index] = translate_c_type(state, result, param, pkg, record_mapping, type_mapping)
|
||||
if !types.is_valid(params[index]) {
|
||||
delete(params, state.allocator)
|
||||
type_mapping[value] = types.INVALID
|
||||
return types.INVALID
|
||||
}
|
||||
}
|
||||
result_type := translate_c_type(state, result, item.child, pkg, record_mapping, type_mapping)
|
||||
if types.is_valid(result_type) {
|
||||
translated = types.function(&state.module.type_store, params, result_type, true, item.variadic)
|
||||
}
|
||||
delete(params, state.allocator)
|
||||
case .Record:
|
||||
if int(item.record) < len(record_mapping) {
|
||||
translated = record_mapping[item.record]
|
||||
@@ -189,6 +209,93 @@ function_signatures_equal :: proc(left: ast.Function, params: []ast.Param, resul
|
||||
return true
|
||||
}
|
||||
|
||||
RECORD_DEPENDENCY_PENDING :: "C record contains an incomplete or unsupported record field"
|
||||
|
||||
record_layout_reason :: proc(
|
||||
store: ^types.Store,
|
||||
record: cimport.Record,
|
||||
fields: []types.Field,
|
||||
selected: target.Target,
|
||||
) -> string {
|
||||
if len(fields) == 0 {
|
||||
if record.size > 0 {
|
||||
return "anonymous C record fields are not supported"
|
||||
}
|
||||
return "empty C records are not supported"
|
||||
}
|
||||
if len(fields) != len(record.fields) || record.alignment == 0 {
|
||||
return "C record metadata is incomplete"
|
||||
}
|
||||
size: u64
|
||||
alignment: u64 = 1
|
||||
if record.kind == .Union {
|
||||
for field in fields {
|
||||
if !types.is_runtime_value(field.type, store) {
|
||||
return RECORD_DEPENDENCY_PENDING
|
||||
}
|
||||
if field.offset != 0 {
|
||||
return "non-natural C record layouts are not supported"
|
||||
}
|
||||
size = max(size, types.size(field.type, store, selected))
|
||||
alignment = max(alignment, u64(types.alignment_of(field.type, store, selected)))
|
||||
}
|
||||
} else {
|
||||
offset: u64
|
||||
for field in fields {
|
||||
if !types.is_runtime_value(field.type, store) {
|
||||
return RECORD_DEPENDENCY_PENDING
|
||||
}
|
||||
field_alignment := u64(types.alignment_of(field.type, store, selected))
|
||||
offset = (offset+field_alignment-1)/field_alignment*field_alignment
|
||||
if field.offset != offset {
|
||||
if field.offset < offset {
|
||||
return "packed C records are not supported"
|
||||
}
|
||||
return "non-natural C record layouts are not supported"
|
||||
}
|
||||
offset += types.size(field.type, store, selected)
|
||||
alignment = max(alignment, field_alignment)
|
||||
}
|
||||
size = offset
|
||||
}
|
||||
size = (size+alignment-1)/alignment*alignment
|
||||
if u64(record.alignment) > alignment {
|
||||
return "over-aligned C records are not supported"
|
||||
}
|
||||
if u64(record.alignment) < alignment || record.size < size {
|
||||
return "packed C records are not supported"
|
||||
}
|
||||
if record.size != size {
|
||||
return "non-natural C record layouts are not supported"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
c_record_by_value_reason :: proc(result: ^cimport.Result, value: cimport.Type_Id, depth := 0) -> string {
|
||||
if depth > 64 || value == cimport.INVALID_TYPE || int(value) < 0 || int(value) >= len(result.types) {
|
||||
return ""
|
||||
}
|
||||
item := result.types[value]
|
||||
#partial switch item.kind {
|
||||
case .Pointer:
|
||||
return ""
|
||||
case .Array:
|
||||
return c_record_by_value_reason(result, item.child, depth+1)
|
||||
case .Record:
|
||||
if int(item.record) >= len(result.records) {
|
||||
return "C record metadata is incomplete"
|
||||
}
|
||||
record := result.records[item.record]
|
||||
if len(record.reason) > 0 {
|
||||
return record.reason
|
||||
}
|
||||
if !record.complete {
|
||||
return "incomplete C records are pointer-only"
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
load_header :: proc(state: ^State, path: string, import_span: source.Span) -> ast.Package_Id {
|
||||
canonical, ok := filepath.abs(path, state.allocator)
|
||||
if !ok {
|
||||
@@ -229,7 +336,7 @@ load_header :: proc(state: ^State, path: string, import_span: source.Span) -> as
|
||||
name = fmt.tprintf("__c_record_%d", len(state.record_types))
|
||||
}
|
||||
record_type = types.named(&state.module.type_store, u32(pkg_id), u32(symbol.intern(state.symbols, name)))
|
||||
_ = types.define_struct(&state.module.type_store, record_type, nil, true, true)
|
||||
_ = types.define_record(&state.module.type_store, record_type, nil, true, true, record.kind == .Union)
|
||||
append(&state.record_identities, strings.clone(record.identity, state.allocator))
|
||||
append(&state.record_types, record_type)
|
||||
}
|
||||
@@ -238,6 +345,42 @@ load_header :: proc(state: ^State, path: string, import_span: source.Span) -> as
|
||||
type_mapping := make([]types.Type, len(result.types), state.allocator)
|
||||
defer delete(type_mapping, state.allocator)
|
||||
|
||||
for _ in 0..<len(result.records) {
|
||||
changed := false
|
||||
for record, record_index in result.records {
|
||||
if !record.complete || len(record.reason) > 0 {
|
||||
continue
|
||||
}
|
||||
record_type := record_mapping[record_index]
|
||||
item, ok := types.node(&state.module.type_store, record_type)
|
||||
if !ok || (item.declared && !item.opaque) {
|
||||
continue
|
||||
}
|
||||
fields := make([]types.Field, len(record.fields), state.allocator)
|
||||
for field, field_index in record.fields {
|
||||
fields[field_index] = types.Field{
|
||||
name=u32(symbol.intern(state.symbols, field.name)),
|
||||
type=translate_c_type(state, &result, field.type, pkg_id, record_mapping, type_mapping),
|
||||
offset=field.offset,
|
||||
}
|
||||
}
|
||||
layout_reason := record_layout_reason(&state.module.type_store, record, fields, state.selected)
|
||||
if len(layout_reason) == 0 {
|
||||
changed = types.define_record(
|
||||
&state.module.type_store, record_type, fields, true, false,
|
||||
record.kind == .Union, record.size, record.alignment,
|
||||
) || changed
|
||||
} else if layout_reason != RECORD_DEPENDENCY_PENDING && len(record.reason) == 0 {
|
||||
delete(result.records[record_index].reason, result.allocator)
|
||||
result.records[record_index].reason = fmt.aprintf("%s", layout_reason, allocator=result.allocator)
|
||||
}
|
||||
delete(fields, state.allocator)
|
||||
}
|
||||
if !changed {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for alias in result.aliases {
|
||||
name := symbol.intern(state.symbols, alias.name)
|
||||
id := types.named(&state.module.type_store, u32(pkg_id), u32(name))
|
||||
@@ -263,17 +406,28 @@ load_header :: proc(state: ^State, path: string, import_span: source.Span) -> as
|
||||
}
|
||||
function_result := translate_c_type(state, &result, function.result, pkg_id, record_mapping, type_mapping)
|
||||
unsupported_reason := function.reason
|
||||
if len(unsupported_reason) == 0 {
|
||||
for param_type in function.params {
|
||||
if reason := c_record_by_value_reason(&result, param_type); len(reason) > 0 {
|
||||
unsupported_reason = reason
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(unsupported_reason) == 0 {
|
||||
unsupported_reason = c_record_by_value_reason(&result, function.result)
|
||||
}
|
||||
if len(unsupported_reason) == 0 {
|
||||
for param in params {
|
||||
if types.contains_c_struct_by_value(param.type, &state.module.type_store) {
|
||||
unsupported_reason = "C records passed by value are not supported"
|
||||
unsupported_reason = "C record parameter is incomplete or has an unsupported layout"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(unsupported_reason) == 0 &&
|
||||
types.contains_c_struct_by_value(function_result, &state.module.type_store) {
|
||||
unsupported_reason = "C records returned by value are not supported"
|
||||
unsupported_reason = "C record result is incomplete or has an unsupported layout"
|
||||
}
|
||||
name := symbol.intern(state.symbols, function.name)
|
||||
duplicate := false
|
||||
@@ -522,7 +676,7 @@ canonical_type :: proc(
|
||||
mapping[index] = value if !types.is_valid(resolved) else resolved
|
||||
return mapping[index]
|
||||
}
|
||||
if item.kind == .Struct {
|
||||
if item.kind == .Struct || item.kind == .Union {
|
||||
mapping[index] = value
|
||||
fields := types.fields_for(&module.type_store, value)
|
||||
for &field in fields {
|
||||
@@ -530,6 +684,16 @@ canonical_type :: proc(
|
||||
}
|
||||
return value
|
||||
}
|
||||
if item.kind == .Function {
|
||||
params := make([]types.Type, int(item.field_count), context.temp_allocator)
|
||||
for param, param_index in types.params_for(&module.type_store, value) {
|
||||
params[param_index] = canonical_type(module, param.type, mapping, visiting)
|
||||
}
|
||||
result := canonical_type(module, item.child, mapping, visiting)
|
||||
resolved := types.function(&module.type_store, params, result, item.c_abi, item.variadic)
|
||||
mapping[index] = resolved
|
||||
return resolved
|
||||
}
|
||||
if types.is_valid(item.child) {
|
||||
item.child = canonical_type(module, item.child, mapping, visiting)
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
|
||||
args[index] = lower_nested_expr(state, arg)
|
||||
}
|
||||
return append_instruction(state, ir.Instruction{
|
||||
op=.Aggregate, span=expr.span, type=expr.type, args=args,
|
||||
op=.Aggregate, span=expr.span, type=expr.type, args=args, integer=expr.integer,
|
||||
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
@@ -332,6 +332,19 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
||||
})
|
||||
}
|
||||
_ = pop(&stack)
|
||||
case .Function:
|
||||
function := hir.as_function(expr.target)
|
||||
if function == hir.INVALID_FUNCTION || int(function) >= len(state.hir_module.functions) {
|
||||
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
||||
} else {
|
||||
last = append_instruction(state, ir.Instruction{
|
||||
op=.Function_Address, span=expr.span, type=expr.type,
|
||||
target=ir.function_ref(ir.Function_Id(function)),
|
||||
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
_ = pop(&stack)
|
||||
case .Widen, .C_Vararg_Promote, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer:
|
||||
stack[frame_index].stage = 1
|
||||
append(&stack, Lower_Expr_Frame{expr=expr.left})
|
||||
@@ -343,7 +356,17 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
||||
append(&stack, Lower_Expr_Frame{expr=expr.left})
|
||||
case .Call:
|
||||
function := hir.as_function(expr.target)
|
||||
if function == hir.INVALID_FUNCTION || int(function) >= len(state.hir_module.functions) {
|
||||
if function == hir.INVALID_FUNCTION {
|
||||
if expr.left == hir.INVALID_EXPR {
|
||||
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
stack[frame_index].stage = 6
|
||||
append(&stack, Lower_Expr_Frame{expr=expr.left})
|
||||
continue
|
||||
}
|
||||
if int(function) >= len(state.hir_module.functions) {
|
||||
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
@@ -356,6 +379,15 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
||||
}
|
||||
continue
|
||||
}
|
||||
if frame.stage == 6 {
|
||||
stack[frame_index].left = last
|
||||
stack[frame_index].args = make([]ir.Instruction_Id, len(expr.args), state.allocator)
|
||||
stack[frame_index].stage = 4
|
||||
if len(expr.args) > 0 {
|
||||
append(&stack, Lower_Expr_Frame{expr=expr.args[0]})
|
||||
}
|
||||
continue
|
||||
}
|
||||
if frame.stage == 5 {
|
||||
last = append_instruction(state, ir.Instruction{
|
||||
op=.Neg_Checked, span=expr.span, type=expr.type, target=ir.INVALID_REF,
|
||||
@@ -405,9 +437,14 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
||||
continue
|
||||
}
|
||||
}
|
||||
function := hir.as_function(expr.target)
|
||||
callee := frame.left
|
||||
if function != hir.INVALID_FUNCTION {
|
||||
callee = ir.INVALID_INSTRUCTION
|
||||
}
|
||||
last = append_instruction(state, ir.Instruction{
|
||||
op=.Call, span=expr.span, type=expr.type, target=ir.function_ref(ir.Function_Id(hir.as_function(expr.target))),
|
||||
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, args=stack[frame_index].args, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
op=.Call, span=expr.span, type=expr.type, target=ir.function_ref(ir.Function_Id(function)),
|
||||
a=callee, b=ir.INVALID_INSTRUCTION, args=stack[frame_index].args, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
stack[frame_index].args = nil
|
||||
_ = pop(&stack)
|
||||
|
||||
@@ -90,7 +90,7 @@ is_type_token :: proc(kind: token.Kind) -> bool {
|
||||
.Keyword_C_Short, .Keyword_C_Ushort, .Keyword_C_Int, .Keyword_C_Uint,
|
||||
.Keyword_C_Long, .Keyword_C_Ulong, .Keyword_C_Longlong, .Keyword_C_Ulonglong,
|
||||
.Keyword_C_Float, .Keyword_C_Double, .Keyword_C_Longdouble,
|
||||
.Keyword_Void, .Identifier, .Question, .At, .Star, .Left_Bracket:
|
||||
.Keyword_Void, .Keyword_C_Func, .Identifier, .Question, .At, .Star, .Left_Bracket:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -291,6 +291,25 @@ parse_type :: proc(parser: ^Parser) -> ast.Type_Syntax {
|
||||
case .Keyword_Void:
|
||||
advance(parser)
|
||||
return types.VOID
|
||||
case .Keyword_C_Func:
|
||||
advance(parser)
|
||||
if _, ok := allow(parser, .Left_Paren); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '(' after c_func type")
|
||||
return types.INVALID
|
||||
}
|
||||
params, variadic := parse_params(parser)
|
||||
if _, ok := allow(parser, .Right_Paren); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected ')' after function type parameters")
|
||||
}
|
||||
result := parse_type(parser)
|
||||
param_types := make([]types.Type, len(params), parser.module.allocator)
|
||||
for param, index in params {
|
||||
param_types[index] = param.type
|
||||
}
|
||||
function_type := types.function(&parser.module.type_store, param_types, result, true, variadic)
|
||||
delete(param_types, parser.module.allocator)
|
||||
delete(params, parser.module.allocator)
|
||||
return function_type
|
||||
case .Identifier:
|
||||
first := advance(parser)
|
||||
name := first
|
||||
@@ -334,11 +353,7 @@ skip_parenthesized :: proc(parser: ^Parser) -> source.Span {
|
||||
return span_from(start.span, end.span)
|
||||
}
|
||||
|
||||
parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Token, nesting: int) -> ast.Expr_Id {
|
||||
if nesting >= MAX_EXPRESSION_NESTING {
|
||||
span := skip_parenthesized(parser)
|
||||
return invalid_expr(parser, span, "expression nesting exceeds 256 levels")
|
||||
}
|
||||
parse_call_args :: proc(parser: ^Parser, nesting: int) -> ([]ast.Expr_Id, token.Token) {
|
||||
left_paren := advance(parser)
|
||||
parser.delimiter_depth += 1
|
||||
defer parser.delimiter_depth -= 1
|
||||
@@ -359,6 +374,15 @@ parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Tok
|
||||
source.add(parser.diagnostics, current(parser).span, "expected ')' after call arguments")
|
||||
right_paren = left_paren
|
||||
}
|
||||
return args[:], right_paren
|
||||
}
|
||||
|
||||
parse_call :: proc(parser: ^Parser, qualifier: symbol.Id, first, name: token.Token, nesting: int) -> ast.Expr_Id {
|
||||
if nesting >= MAX_EXPRESSION_NESTING {
|
||||
span := skip_parenthesized(parser)
|
||||
return invalid_expr(parser, span, "expression nesting exceeds 256 levels")
|
||||
}
|
||||
args, right_paren := parse_call_args(parser, nesting)
|
||||
return add_expr(parser, ast.Expr{
|
||||
kind=.Call,
|
||||
span=source.Span{file=name.span.file, start=first.span.start, end=right_paren.span.end},
|
||||
@@ -735,6 +759,24 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
||||
}
|
||||
continue
|
||||
}
|
||||
if current(parser).kind == .Left_Paren {
|
||||
if nesting >= MAX_EXPRESSION_NESTING {
|
||||
span := skip_parenthesized(parser)
|
||||
left = invalid_expr(parser, span, "expression nesting exceeds 256 levels")
|
||||
continue
|
||||
}
|
||||
left_expr := parser.module.exprs[left]
|
||||
args, right_paren := parse_call_args(parser, nesting)
|
||||
left = add_expr(parser, ast.Expr{
|
||||
kind=.Call,
|
||||
span=span_from(left_expr.span, right_paren.span),
|
||||
args=args,
|
||||
left=left,
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
continue
|
||||
}
|
||||
left_power, right_power, ok := infix_binding_power(current(parser).kind)
|
||||
if !ok || left_power < minimum_binding_power {
|
||||
break
|
||||
|
||||
+196
-16
@@ -58,9 +58,11 @@ Kind :: enum u8 {
|
||||
Pointer,
|
||||
Slice,
|
||||
Optional,
|
||||
Function,
|
||||
Named,
|
||||
Alias,
|
||||
Struct,
|
||||
Union,
|
||||
}
|
||||
|
||||
Node :: struct {
|
||||
@@ -68,8 +70,10 @@ Node :: struct {
|
||||
child: Type,
|
||||
count: u64,
|
||||
sentinel: u64,
|
||||
explicit_size: u64,
|
||||
field_start: u32,
|
||||
field_count: u32,
|
||||
explicit_alignment: u32,
|
||||
pkg: u32,
|
||||
name: u32,
|
||||
qualifier: u32,
|
||||
@@ -78,14 +82,17 @@ Node :: struct {
|
||||
many: bool,
|
||||
has_sentinel: bool,
|
||||
inferred_count: bool,
|
||||
c_abi: bool,
|
||||
variadic: bool,
|
||||
c_layout: bool,
|
||||
opaque: bool,
|
||||
declared: bool,
|
||||
}
|
||||
|
||||
Field :: struct {
|
||||
name: u32,
|
||||
type: Type,
|
||||
name: u32,
|
||||
type: Type,
|
||||
offset: u64,
|
||||
}
|
||||
|
||||
Store :: struct {
|
||||
@@ -118,7 +125,7 @@ clone_store :: proc(source: ^Store, allocator := context.allocator) -> Store {
|
||||
}
|
||||
|
||||
intern :: proc(store: ^Store, candidate: Node) -> Type {
|
||||
if candidate.kind != .Struct && candidate.kind != .Named {
|
||||
if candidate.kind != .Struct && candidate.kind != .Union && candidate.kind != .Named {
|
||||
for existing, index in store.nodes {
|
||||
if existing == candidate {
|
||||
return DYNAMIC_START+Type(index)
|
||||
@@ -133,7 +140,7 @@ intern :: proc(store: ^Store, candidate: Node) -> Type {
|
||||
named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xffff_ffff) -> Type {
|
||||
normalized_file := file if qualifier != 0 else u32(0)
|
||||
for existing, index in store.nodes {
|
||||
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Struct) &&
|
||||
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Struct || existing.kind == .Union) &&
|
||||
existing.pkg == pkg && existing.name == name && existing.qualifier == qualifier &&
|
||||
existing.file == normalized_file {
|
||||
return DYNAMIC_START+Type(index)
|
||||
@@ -144,7 +151,7 @@ named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0, file: u32 = 0xf
|
||||
|
||||
find_named :: proc(store: ^Store, pkg, name: u32, qualifier: u32 = 0) -> Type {
|
||||
for existing, index in store.nodes {
|
||||
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Struct) &&
|
||||
if (existing.kind == .Named || existing.kind == .Alias || existing.kind == .Struct || existing.kind == .Union) &&
|
||||
existing.pkg == pkg && existing.name == name && existing.qualifier == qualifier {
|
||||
return DYNAMIC_START+Type(index)
|
||||
}
|
||||
@@ -164,25 +171,53 @@ define_alias :: proc(store: ^Store, id, child: Type) -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
define_struct :: proc(store: ^Store, id: Type, fields: []Field, c_layout, opaque: bool) -> bool {
|
||||
define_record :: proc(
|
||||
store: ^Store,
|
||||
id: Type,
|
||||
fields: []Field,
|
||||
c_layout, opaque: bool,
|
||||
is_union := false,
|
||||
explicit_size: u64 = 0,
|
||||
explicit_alignment: u32 = 0,
|
||||
) -> bool {
|
||||
existing, ok := node(store, id)
|
||||
if !ok || (existing.kind != .Named && existing.kind != .Struct) || existing.declared {
|
||||
if !ok || (existing.kind != .Named && existing.kind != .Struct && existing.kind != .Union) ||
|
||||
(existing.declared && !existing.opaque) {
|
||||
return false
|
||||
}
|
||||
index := int(id-DYNAMIC_START)
|
||||
store.nodes[index].kind = .Struct
|
||||
store.nodes[index].kind = .Union if is_union else .Struct
|
||||
store.nodes[index].c_layout = c_layout
|
||||
store.nodes[index].opaque = opaque
|
||||
store.nodes[index].declared = true
|
||||
store.nodes[index].explicit_size = explicit_size
|
||||
store.nodes[index].explicit_alignment = explicit_alignment
|
||||
store.nodes[index].field_start = u32(len(store.fields))
|
||||
store.nodes[index].field_count = u32(len(fields))
|
||||
append(&store.fields, ..fields)
|
||||
return true
|
||||
}
|
||||
|
||||
define_struct :: proc(store: ^Store, id: Type, fields: []Field, c_layout, opaque: bool) -> bool {
|
||||
return define_record(store, id, fields, c_layout, opaque)
|
||||
}
|
||||
|
||||
fields_for :: proc(store: ^Store, value: Type) -> []Field {
|
||||
item, ok := node(store, value)
|
||||
if !ok || item.kind != .Struct {
|
||||
if !ok || (item.kind != .Struct && item.kind != .Union) {
|
||||
return nil
|
||||
}
|
||||
start := int(item.field_start)
|
||||
end := start+int(item.field_count)
|
||||
if start < 0 || end > len(store.fields) {
|
||||
return nil
|
||||
}
|
||||
return store.fields[start:end]
|
||||
}
|
||||
|
||||
params_for :: proc(store: ^Store, value: Type) -> []Field {
|
||||
item, ok := node(store, value)
|
||||
if !ok || item.kind != .Function {
|
||||
return nil
|
||||
}
|
||||
start := int(item.field_start)
|
||||
@@ -350,7 +385,7 @@ is_concrete :: proc(value: Type, store: ^Store = nil) -> bool {
|
||||
value_kind == .Slice || value_kind == .Optional {
|
||||
return true
|
||||
}
|
||||
if value_kind == .Struct {
|
||||
if value_kind == .Struct || value_kind == .Union {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.declared
|
||||
}
|
||||
@@ -373,10 +408,64 @@ is_optional :: proc(value: Type, store: ^Store) -> bool {
|
||||
return kind(value, store) == .Optional
|
||||
}
|
||||
|
||||
is_function :: proc(value: Type, store: ^Store) -> bool {
|
||||
return kind(value, store) == .Function
|
||||
}
|
||||
|
||||
is_struct :: proc(value: Type, store: ^Store) -> bool {
|
||||
return kind(value, store) == .Struct
|
||||
}
|
||||
|
||||
is_record :: proc(value: Type, store: ^Store) -> bool {
|
||||
value_kind := kind(value, store)
|
||||
return value_kind == .Struct || value_kind == .Union
|
||||
}
|
||||
|
||||
is_union :: proc(value: Type, store: ^Store) -> bool {
|
||||
return kind(value, store) == .Union
|
||||
}
|
||||
|
||||
resolve_alias :: proc(value: Type, store: ^Store, depth := 0) -> Type {
|
||||
if depth > 64 {
|
||||
return INVALID
|
||||
}
|
||||
item, ok := node(store, value)
|
||||
if !ok || item.kind != .Alias {
|
||||
return value
|
||||
}
|
||||
return resolve_alias(item.child, store, depth+1)
|
||||
}
|
||||
|
||||
is_c_record_field_type :: proc(value: Type, store: ^Store, depth := 0) -> bool {
|
||||
if depth > 256 {
|
||||
return false
|
||||
}
|
||||
resolved := resolve_alias(value, store)
|
||||
if is_concrete_scalar(resolved) || is_pointer(resolved, store) || is_optional_pointer(resolved, store) {
|
||||
return true
|
||||
}
|
||||
item, ok := node(store, resolved)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if item.kind == .Array {
|
||||
return item.count > 0 && !item.has_sentinel && !item.inferred_count &&
|
||||
is_c_record_field_type(item.child, store, depth+1)
|
||||
}
|
||||
if item.kind != .Struct && item.kind != .Union {
|
||||
return false
|
||||
}
|
||||
if !item.c_layout || !item.declared || item.opaque || item.field_count == 0 {
|
||||
return false
|
||||
}
|
||||
for field in fields_for(store, resolved) {
|
||||
if !is_c_record_field_type(field.type, store, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
is_optional_pointer :: proc(value: Type, store: ^Store) -> bool {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.kind == .Optional && is_pointer(item.child, store)
|
||||
@@ -390,9 +479,9 @@ is_runtime_value :: proc(value: Type, store: ^Store) -> bool {
|
||||
if value_kind == .Slice || value_kind == .Array || value_kind == .Optional {
|
||||
return !contains_c_struct_by_value(value, store)
|
||||
}
|
||||
if value_kind == .Struct {
|
||||
if value_kind == .Struct || value_kind == .Union {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.declared && !item.opaque && !contains_c_struct_by_value(value, store)
|
||||
return ok && item.declared && !item.opaque && (!item.c_layout || item.field_count > 0)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -409,7 +498,7 @@ contains_c_struct_by_value :: proc(value: Type, store: ^Store, depth := 0) -> bo
|
||||
return false
|
||||
}
|
||||
if item.kind == .Struct {
|
||||
if item.c_layout {
|
||||
if item.opaque || (item.c_layout && item.field_count == 0) {
|
||||
return true
|
||||
}
|
||||
for field in fields_for(store, value) {
|
||||
@@ -419,6 +508,9 @@ contains_c_struct_by_value :: proc(value: Type, store: ^Store, depth := 0) -> bo
|
||||
}
|
||||
return false
|
||||
}
|
||||
if item.kind == .Union {
|
||||
return item.opaque || (item.c_layout && item.field_count == 0)
|
||||
}
|
||||
if item.kind == .Array || item.kind == .Slice || item.kind == .Optional {
|
||||
return contains_c_struct_by_value(item.child, store, depth+1)
|
||||
}
|
||||
@@ -429,7 +521,8 @@ is_c_signature_type :: proc(value: Type, store: ^Store, allow_void := false) ->
|
||||
if allow_void && is_void(value) {
|
||||
return true
|
||||
}
|
||||
return is_concrete_scalar(value) || is_pointer(value, store) || is_optional_pointer(value, store)
|
||||
return is_concrete_scalar(value) || is_pointer(value, store) || is_optional_pointer(value, store) ||
|
||||
(is_c_struct(value, store) && is_runtime_value(value, store))
|
||||
}
|
||||
|
||||
is_c_integer_promotion_candidate :: proc(value: Type) -> bool {
|
||||
@@ -518,9 +611,21 @@ container :: proc(value: Type, store: ^Store) -> (Node, bool) {
|
||||
return array_node, true
|
||||
}
|
||||
|
||||
function_pointer :: proc(value: Type, store: ^Store) -> (pointer_item, function_item: Node, function_type: Type, ok: bool) {
|
||||
pointer_node, pointer_ok := node(store, value)
|
||||
if !pointer_ok || pointer_node.kind != .Pointer {
|
||||
return {}, {}, INVALID, false
|
||||
}
|
||||
function_node, function_ok := node(store, pointer_node.child)
|
||||
if !function_ok || function_node.kind != .Function {
|
||||
return {}, {}, INVALID, false
|
||||
}
|
||||
return pointer_node, function_node, pointer_node.child, true
|
||||
}
|
||||
|
||||
is_c_struct :: proc(value: Type, store: ^Store) -> bool {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.kind == .Struct && item.c_layout
|
||||
return ok && (item.kind == .Struct || item.kind == .Union) && item.c_layout
|
||||
}
|
||||
|
||||
pointer :: proc(
|
||||
@@ -572,6 +677,47 @@ optional :: proc(store: ^Store, child: Type) -> Type {
|
||||
return intern(store, Node{kind=.Optional, child=child})
|
||||
}
|
||||
|
||||
function_params_equal :: proc(store: ^Store, item: Node, params: []Type) -> bool {
|
||||
if item.field_count != u32(len(params)) {
|
||||
return false
|
||||
}
|
||||
start := int(item.field_start)
|
||||
end := start+int(item.field_count)
|
||||
if start < 0 || end > len(store.fields) {
|
||||
return false
|
||||
}
|
||||
for param, index in params {
|
||||
if store.fields[start+index].type != param {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
function :: proc(store: ^Store, params: []Type, result: Type, c_abi, variadic: bool) -> Type {
|
||||
for existing, index in store.nodes {
|
||||
if existing.kind == .Function &&
|
||||
existing.child == result &&
|
||||
existing.c_abi == c_abi &&
|
||||
existing.variadic == variadic &&
|
||||
function_params_equal(store, existing, params) {
|
||||
return DYNAMIC_START+Type(index)
|
||||
}
|
||||
}
|
||||
start := len(store.fields)
|
||||
for param in params {
|
||||
append(&store.fields, Field{type=param})
|
||||
}
|
||||
return intern(store, Node{
|
||||
kind=.Function,
|
||||
child=result,
|
||||
field_start=u32(start),
|
||||
field_count=u32(len(params)),
|
||||
c_abi=c_abi,
|
||||
variadic=variadic,
|
||||
})
|
||||
}
|
||||
|
||||
with_array_count :: proc(store: ^Store, value: Type, count: u64) -> Type {
|
||||
item, ok := node(store, value)
|
||||
if !ok || item.kind != .Array {
|
||||
@@ -638,7 +784,7 @@ can_decay_array_pointer :: proc(from, to: Type, store: ^Store) -> bool {
|
||||
|
||||
is_opaque_struct :: proc(value: Type, store: ^Store) -> bool {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.kind == .Struct && item.opaque
|
||||
return ok && (item.kind == .Struct || item.kind == .Union) && item.opaque
|
||||
}
|
||||
|
||||
size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
||||
@@ -660,7 +806,13 @@ size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
||||
child_size := size(item.child, store, selected)
|
||||
child_align := u64(alignment_of(item.child, store, selected))
|
||||
return ((child_size+1+child_align-1)/child_align)*child_align
|
||||
case .Function:
|
||||
return 0
|
||||
case .Struct:
|
||||
item, _ := node(store, value)
|
||||
if item.explicit_size > 0 {
|
||||
return item.explicit_size
|
||||
}
|
||||
offset: u64
|
||||
max_align: u64 = 1
|
||||
for field in fields_for(store, value) {
|
||||
@@ -670,6 +822,18 @@ size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
|
||||
max_align = max(max_align, field_align)
|
||||
}
|
||||
return (offset+max_align-1)/max_align*max_align
|
||||
case .Union:
|
||||
item, _ := node(store, value)
|
||||
if item.explicit_size > 0 {
|
||||
return item.explicit_size
|
||||
}
|
||||
result: u64
|
||||
max_align: u64 = 1
|
||||
for field in fields_for(store, value) {
|
||||
result = max(result, size(field.type, store, selected))
|
||||
max_align = max(max_align, u64(alignment_of(field.type, store, selected)))
|
||||
}
|
||||
return (result+max_align-1)/max_align*max_align
|
||||
case:
|
||||
return 0
|
||||
}
|
||||
@@ -683,7 +847,23 @@ alignment_of :: proc(value: Type, store: ^Store, selected := target.DEFAULT) ->
|
||||
return target.pointer_bits(selected)/8
|
||||
case .Array, .Optional:
|
||||
return alignment_of(child_type(value, store), store, selected)
|
||||
case .Function:
|
||||
return 1
|
||||
case .Struct:
|
||||
item, _ := node(store, value)
|
||||
if item.explicit_alignment > 0 {
|
||||
return int(item.explicit_alignment)
|
||||
}
|
||||
result := 1
|
||||
for field in fields_for(store, value) {
|
||||
result = max(result, alignment_of(field.type, store, selected))
|
||||
}
|
||||
return result
|
||||
case .Union:
|
||||
item, _ := node(store, value)
|
||||
if item.explicit_alignment > 0 {
|
||||
return int(item.explicit_alignment)
|
||||
}
|
||||
result := 1
|
||||
for field in fields_for(store, value) {
|
||||
result = max(result, alignment_of(field.type, store, selected))
|
||||
|
||||
Reference in New Issue
Block a user