Implement basic arguments
All checks were successful
CI / ci (ReleaseFast, ubuntu-latest) (push) Successful in 34s
CI / ci (ReleaseSmall, ubuntu-latest) (push) Successful in 34s
CI / ci (ReleaseSafe, ubuntu-latest) (push) Successful in 42s
CI / ci (Debug, ubuntu-latest) (push) Successful in 56s

This commit is contained in:
David Gonzalez Martin 2025-04-09 15:47:01 -06:00
parent 143fa2d03b
commit 9e503650d2
2 changed files with 591 additions and 59 deletions

View File

@ -114,7 +114,7 @@ pub const Variable = struct {
pub const Local = struct { pub const Local = struct {
variable: Variable, variable: Variable,
is_argument: bool, argument_index: ?u32,
pub const Buffer = struct { pub const Buffer = struct {
buffer: lib.VirtualBuffer(Local), buffer: lib.VirtualBuffer(Local),
@ -250,7 +250,7 @@ pub const Type = struct {
const subroutine_type = module.llvm.di_builder.create_subroutine_type(module.llvm.file, semantic_debug_argument_types, .{}); const subroutine_type = module.llvm.di_builder.create_subroutine_type(module.llvm.file, semantic_debug_argument_types, .{});
break :b subroutine_type.to_type(); break :b subroutine_type.to_type();
}, },
.pointer => |pointer| module.llvm.di_builder.create_pointer_type(pointer.type.llvm.debug.?, 64, 64, 0, ty.name).to_type(), .pointer => |pointer| module.llvm.di_builder.create_pointer_type(pointer.type.resolve(module).debug, 64, 64, 0, ty.name).to_type(),
else => @trap(), else => @trap(),
} else null; } else null;
ty.llvm.debug = debug_type; ty.llvm.debug = debug_type;
@ -431,9 +431,21 @@ pub const Statement = struct {
bb: union(enum) { bb: union(enum) {
local: *Local, local: *Local,
@"return": ?*Value, @"return": ?*Value,
assignment: Assignment,
expression: *Value,
}, },
line: u32, line: u32,
column: u32, column: u32,
const Assignment = struct {
left: *Value,
right: *Value,
kind: Operator,
const Operator = enum {
@"=",
};
};
}; };
const Unary = struct { const Unary = struct {
@ -488,6 +500,7 @@ const Binary = struct {
pub const Call = struct { pub const Call = struct {
callable: *Value, callable: *Value,
arguments: []const *Value, arguments: []const *Value,
function_type: *Type = undefined,
}; };
pub const Value = struct { pub const Value = struct {
@ -498,9 +511,11 @@ pub const Value = struct {
binary: Binary, binary: Binary,
variable_reference: *Variable, variable_reference: *Variable,
local, local,
argument,
intrinsic: Intrinsic, intrinsic: Intrinsic,
dereference: *Value, dereference: *Value,
call: Call, call: Call,
infer_or_ignore,
}, },
type: ?*Type = null, type: ?*Type = null,
llvm: ?*llvm.Value = null, llvm: ?*llvm.Value = null,
@ -720,6 +735,7 @@ pub const Module = struct {
pointer_types: IndexBuffer, pointer_types: IndexBuffer,
void_type: *Type, void_type: *Type,
noreturn_type: *Type, noreturn_type: *Type,
void_value: *Value,
lexical_blocks: lib.VirtualBuffer(LexicalBlock), lexical_blocks: lib.VirtualBuffer(LexicalBlock),
statements: lib.VirtualBuffer(Statement), statements: lib.VirtualBuffer(Statement),
current_function: ?*Global = null, current_function: ?*Global = null,
@ -1040,6 +1056,15 @@ pub const Module = struct {
return ty; return ty;
} }
}, },
'&' => {
module.offset += 1;
module.skip_space();
const element_type = module.parse_type();
const pointer_type = module.get_pointer_type(.{
.type = element_type,
});
return pointer_type;
},
else => @trap(), else => @trap(),
} }
} }
@ -1632,7 +1657,7 @@ pub const Module = struct {
const statement_start_character = module.content[module.offset]; const statement_start_character = module.content[module.offset];
const statement = module.statements.add(); const statement = module.statements.add();
const require_semicolon = true; var require_semicolon = true;
statement.* = .{ statement.* = .{
.bb = switch (statement_start_character) { .bb = switch (statement_start_character) {
'>' => blk: { '>' => blk: {
@ -1658,7 +1683,7 @@ pub const Module = struct {
.column = statement_column, .column = statement_column,
.scope = module.current_scope, .scope = module.current_scope,
}, },
.is_argument = false, .argument_index = null,
}; };
assert(module.current_scope == &block.scope); assert(module.current_scope == &block.scope);
_ = block.locals.append(local); _ = block.locals.append(local);
@ -1685,9 +1710,43 @@ pub const Module = struct {
@trap(); @trap();
}, },
} }
} } else {
module.offset -= statement_start_identifier.len;
const left = module.parse_value(.{
.kind = .left,
});
@trap(); module.skip_space();
if (module.consume_character_if_match(';')) {
require_semicolon = false;
break :blk .{
.expression = left,
};
} else {
const operator_start_character = module.content[module.offset];
const operator: Statement.Assignment.Operator = switch (operator_start_character) {
'=' => .@"=",
else => @trap(),
};
module.offset += switch (operator) {
.@"=" => 1,
};
module.skip_space();
const right = module.parse_value(.{});
break :blk .{
.assignment = .{
.left = left,
.right = right,
.kind = operator,
},
};
}
}
}, },
else => @trap(), else => @trap(),
}, },
@ -1914,22 +1973,26 @@ pub const Module = struct {
var semantic_argument_buffer: [64]*Value = undefined; var semantic_argument_buffer: [64]*Value = undefined;
_ = &semantic_argument_buffer; _ = &semantic_argument_buffer;
while (true) { while (true) : (semantic_argument_count += 1) {
module.skip_space(); module.skip_space();
if (module.consume_character_if_match(right_parenthesis)) { if (module.consume_character_if_match(right_parenthesis)) {
break; break;
} }
// const argument = module.parse_value(.{}); const argument = module.parse_value(.{});
// const argument_index = semantic_argument_count; const argument_index = semantic_argument_count;
// semantic_argument_buffer[argument_index] = argument; semantic_argument_buffer[argument_index] = argument;
// semantic_argument_count = argument_index + 1;
@trap(); module.skip_space();
_ = module.consume_character_if_match(',');
} }
const arguments: []const *Value = if (semantic_argument_count != 0) { const arguments: []const *Value = if (semantic_argument_count != 0) blk: {
@trap(); const arguments = module.arena.allocate(*Value, semantic_argument_count);
@memcpy(arguments, semantic_argument_buffer[0..semantic_argument_count]);
break :blk arguments;
} else &.{}; } else &.{};
const call = module.values.add(); const call = module.values.add();
@ -2065,20 +2128,40 @@ pub const Module = struct {
module.expect_character(left_parenthesis); module.expect_character(left_parenthesis);
var semantic_argument_count: u32 = 0; var semantic_argument_count: u32 = 0;
var semantic_argument_type_buffer: [64]*Type = undefined;
var semantic_argument_name_buffer: [64][]const u8 = undefined;
while (module.offset < module.content.len and module.content[module.offset] != right_parenthesis) : (semantic_argument_count += 1) { while (module.offset < module.content.len and module.content[module.offset] != right_parenthesis) : (semantic_argument_count += 1) {
module.skip_space(); module.skip_space();
@trap(); const argument_name = module.parse_identifier();
semantic_argument_name_buffer[semantic_argument_count] = argument_name;
module.skip_space();
module.expect_character(':');
module.skip_space();
const argument_type = module.parse_type();
semantic_argument_type_buffer[semantic_argument_count] = argument_type;
module.skip_space();
if (module.consume_character_if_match(',')) {
module.skip_space();
}
} }
module.expect_character(right_parenthesis); module.expect_character(right_parenthesis);
module.skip_space(); module.skip_space();
const return_type = module.parse_type(); const return_type = module.parse_type();
// TODO const argument_types: []const *Type = if (semantic_argument_count == 0) &.{} else blk: {
assert(semantic_argument_count == 0); const argument_types = module.arena.allocate(*Type, semantic_argument_count);
const argument_types: []const *Type = &.{}; @memcpy(argument_types, semantic_argument_type_buffer[0..argument_types.len]);
break :blk argument_types;
};
module.skip_space(); module.skip_space();
@ -2134,6 +2217,26 @@ pub const Module = struct {
}, },
}; };
if (semantic_argument_count != 0) {
const arguments = module.arena.allocate(*Local, semantic_argument_count);
storage.bb.function.arguments = arguments;
for (argument_types, semantic_argument_name_buffer[0..semantic_argument_count], arguments, 0..) |argument_type, argument_name, *argument, argument_index| {
const result = module.locals.add();
argument.* = result;
result.* = .{
.variable = .{
.line = 0,
.column = 0,
.name = argument_name,
.scope = &storage.bb.function.scope,
.type = argument_type,
.initial_value = undefined,
},
.argument_index = @intCast(argument_index),
};
}
}
const global_scope = module.current_scope; const global_scope = module.current_scope;
module.current_scope = &storage.bb.function.scope; module.current_scope = &storage.bb.function.scope;
defer module.current_scope = global_scope; defer module.current_scope = global_scope;
@ -2514,20 +2617,15 @@ pub const Module = struct {
else => @trap(), else => @trap(),
}; };
// TODO: delete this const storage = module.values.add();
storage.* = .{
// const argument_value = module.values.add(); .bb = .argument,
// argument_value.* = .{ .type = module.get_pointer_type(.{
// .llvm = semantic_argument_storage, .type = argument_variable.variable.type.?,
// .type = argument_variable.variable.value.type, }),
// .bb = .argument, .llvm = semantic_argument_storage,
// .lvalue = true, };
// .dereference_to_assign = false, argument_variable.variable.storage = storage;
// };
// argument_variable.* = .{
// .value = argument_value,
// .name = argument_variable.name,
// };
// no pointer // no pointer
const argument_type = argument_variable.variable.storage.?.type.?.bb.pointer.type; const argument_type = argument_variable.variable.storage.?.type.?.bb.pointer.type;
@ -2726,7 +2824,7 @@ pub const Module = struct {
assert(value.llvm == null); assert(value.llvm == null);
const llvm_value: *llvm.Value = switch (value.bb) { const llvm_value: *llvm.Value = switch (value.bb) {
.constant_integer => |constant_integer| value_type.llvm.handle.?.to_integer().get_constant(constant_integer.value, @intFromBool(constant_integer.signed)).to_value(), .constant_integer => |constant_integer| value_type.resolve(module).handle.to_integer().get_constant(constant_integer.value, @intFromBool(constant_integer.signed)).to_value(),
.unary => |unary| switch (unary.id) { .unary => |unary| switch (unary.id) {
.@"-" => blk: { .@"-" => blk: {
const unary_value = unary.value.llvm orelse b: { const unary_value = unary.value.llvm orelse b: {
@ -2850,19 +2948,379 @@ pub const Module = struct {
}; };
break :blk result; break :blk result;
}, },
.call => |call| blk: { .call => |call| c: {
const llvm_callable = switch (call.callable.bb) { const llvm_callable = switch (call.callable.bb) {
.variable_reference => |variable| variable.storage.?.llvm.?, .variable_reference => |variable| variable.storage.?.llvm.?,
else => @trap(), else => @trap(),
}; };
const function_type = switch (call.callable.bb) { const raw_function_type = switch (call.callable.bb) {
.variable_reference => |variable| variable.storage.?.type.?.bb.pointer.type, .variable_reference => |variable| variable.storage.?.type.?.bb.pointer.type,
else => @trap(), else => @trap(),
}; };
const llvm_function_type = function_type.resolve(module).handle; // const llvm_function_type = raw_function_type.resolve(module).handle;
const llvm_call = module.llvm.builder.create_call(llvm_function_type.to_function(), llvm_callable, &.{}); // const llvm_call = module.llvm.builder.create_call(llvm_function_type.to_function(), llvm_callable, &.{});
break :blk llvm_call;
}, // const llvm_callable = switch (child_type == raw_function_type) {
// true => may_be_callable.llvm,
// else => module.create_load(.{ .type = pointer_type, .value = may_be_callable.llvm }),
// };
const function_type = &raw_function_type.bb.function;
const calling_convention = function_type.calling_convention;
const llvm_calling_convention = calling_convention.to_llvm();
var llvm_abi_argument_value_buffer: [64]*llvm.Value = undefined;
var llvm_abi_argument_type_buffer: [64]*llvm.Type = undefined;
var abi_argument_type_buffer: [64]*Type = undefined;
var argument_type_abi_buffer: [64]Abi.Information = undefined;
var abi_argument_count: u16 = 0;
const function_semantic_argument_count = function_type.argument_abis.len;
// TODO
const uses_in_alloca = false;
if (uses_in_alloca) {
@trap();
}
const llvm_indirect_return_value: *llvm.Value = switch (function_type.return_abi.flags.kind) {
.indirect, .in_alloca, .coerce_and_expand => blk: {
// TODO: handle edge cases:
// - virtual function pointer thunk
// - return alloca already exists
const temporal_alloca = module.create_alloca(.{ .type = function_type.return_abi.semantic_type, .name = "tmp" });
const has_sret = function_type.return_abi.flags.kind == .indirect;
if (has_sret) {
llvm_abi_argument_value_buffer[abi_argument_count] = temporal_alloca;
abi_argument_type_buffer[abi_argument_count] = module.void_type;
llvm_abi_argument_type_buffer[abi_argument_count] = module.void_type.llvm.handle.?;
abi_argument_count += 1;
break :blk temporal_alloca;
} else if (function_type.return_abi.flags.kind == .in_alloca) {
@trap();
} else {
@trap();
}
},
else => undefined,
};
_ = llvm_indirect_return_value;
var available_registers = function_type.available_registers;
for (call.arguments, 0..) |semantic_argument_value, semantic_argument_index| {
const is_named_argument = semantic_argument_index < function_semantic_argument_count;
// const expected_semantic_argument_type: ?*Type = if (is_named_argument) function_type.argument_abis[semantic_argument_index].semantic_type else null;
module.emit_value(function, semantic_argument_value);
const semantic_argument_type = switch (is_named_argument) {
true => function_type.argument_abis[semantic_argument_index].semantic_type,
false => @trap(), // TODO: below
// if (semantic_argument_value.lvalue and semantic_argument_value.dereference_to_assign) blk: {
// const t = semantic_argument_value.type;
// assert(t.bb == .pointer);
// assert(t.bb.pointer.type.bb == .structure);
// break :blk t.bb.pointer.type;
// } else semantic_argument_value.type,
};
const argument_abi = if (is_named_argument) function_type.argument_abis[semantic_argument_index] else Abi.SystemV.classify_argument(module, &available_registers, &llvm_abi_argument_type_buffer, &abi_argument_type_buffer, .{
.type = semantic_argument_type,
.abi_start = abi_argument_count,
.is_named_argument = true,
});
if (is_named_argument) {
for (llvm_abi_argument_type_buffer[argument_abi.abi_start..][0..argument_abi.abi_count], abi_argument_type_buffer[argument_abi.abi_start..][0..argument_abi.abi_count], function_type.abi_argument_types[argument_abi.abi_start..][0..argument_abi.abi_count]) |*llvm_t, *t, abi_argument_type| {
llvm_t.* = abi_argument_type.llvm.handle.?;
t.* = abi_argument_type;
}
}
argument_type_abi_buffer[semantic_argument_index] = argument_abi;
if (argument_abi.padding.type) |padding_type| {
_ = padding_type;
@trap();
}
assert(abi_argument_count == argument_abi.abi_start);
const argument_abi_kind = argument_abi.flags.kind;
switch (argument_abi_kind) {
.direct, .extend => {
const coerce_to_type = argument_abi.get_coerce_to_type();
if (coerce_to_type.bb != .structure and semantic_argument_type.is_abi_equal(coerce_to_type) and argument_abi.attributes.direct.offset == 0) {
var v = switch (argument_abi.semantic_type.get_evaluation_kind()) {
.aggregate => @trap(),
else => semantic_argument_value,
};
_ = &v;
if (!coerce_to_type.is_abi_equal(v.type.?)) {
switch (v.type.?) {
else => @trap(),
}
}
// TODO: bitcast
// if (argument_abi.abi_start < function_type.argument_type_abis.len and v.type.llvm.handle != abi_arguments
// TODO: fill types
llvm_abi_argument_value_buffer[abi_argument_count] = v.llvm.?;
abi_argument_count += 1;
} else {
if (coerce_to_type.bb == .structure and argument_abi.flags.kind == .direct and !argument_abi.flags.can_be_flattened) {
@trap();
}
const evaluation_kind = semantic_argument_type.get_evaluation_kind();
var src = switch (evaluation_kind) {
.aggregate => semantic_argument_value,
.scalar => {
@trap();
},
.complex => @trap(),
};
src = switch (argument_abi.attributes.direct.offset > 0) {
true => @trap(),
false => src,
};
if (coerce_to_type.bb == .structure and argument_abi.flags.kind == .direct and argument_abi.flags.can_be_flattened) {
const source_type_size_is_scalable = false; // TODO
if (source_type_size_is_scalable) {
@trap();
} else {
const destination_size = coerce_to_type.get_byte_size();
const source_size = argument_abi.semantic_type.get_byte_size();
const alignment = argument_abi.semantic_type.get_byte_alignment();
const source = switch (source_size < destination_size) {
true => blk: {
const temporal_alloca = module.create_alloca(.{ .type = coerce_to_type, .name = "coerce", .alignment = alignment });
const destination = temporal_alloca;
const source = semantic_argument_value.llvm.?;
_ = module.llvm.builder.create_memcpy(destination, alignment, source, alignment, module.integer_type(64, false).llvm.handle.?.to_integer().get_constant(semantic_argument_type.get_byte_size(), @intFromBool(false)).to_value());
break :blk temporal_alloca;
},
false => src.llvm,
};
_ = source;
// TODO:
assert(argument_abi.attributes.direct.offset == 0);
@trap();
// switch (semantic_argument_value.lvalue) {
// true => {
// for (coerce_to_type.bb.structure.fields, 0..) |field, field_index| {
// const gep = module.llvm.builder.create_struct_gep(coerce_to_type.llvm.handle.to_struct(), source, @intCast(field_index));
// const maybe_undef = false;
// if (maybe_undef) {
// @trap();
// }
// const load = module.create_load(.{ .value = gep, .type = field.type, .alignment = alignment });
//
// llvm_abi_argument_value_buffer[abi_argument_count] = load;
// abi_argument_count += 1;
// }
// },
// false => {
// for (0..coerce_to_type.bb.structure.fields.len) |field_index| {
// const extract_value = module.llvm.builder.create_extract_value(source, @intCast(field_index));
// llvm_abi_argument_value_buffer[abi_argument_count] = extract_value;
// abi_argument_count += 1;
// }
// },
// }
}
} else {
assert(argument_abi.abi_count == 1);
@trap();
// TODO
// assert(src.type.bb == .pointer);
// const source_type = src.type.bb.pointer.type;
// assert(source_type == argument_abi.semantic_type);
// const destination_type = argument_abi.get_coerce_to_type();
// const load = module.create_coerced_load(src.llvm, source_type, destination_type);
//
// const is_cmse_ns_call = false;
// if (is_cmse_ns_call) {
// @trap();
// }
// const maybe_undef = false;
// if (maybe_undef) {
// @trap();
// }
//
// llvm_abi_argument_value_buffer[abi_argument_count] = load;
// abi_argument_count += 1;
}
}
},
.indirect, .indirect_aliased => indirect: {
if (semantic_argument_type.get_evaluation_kind() == .aggregate) {
const same_address_space = true;
assert(argument_abi.abi_start >= function_type.abi_argument_types.len or same_address_space);
const indirect_alignment = argument_abi.attributes.indirect.alignment;
const address_alignment = semantic_argument_type.get_byte_alignment();
const get_or_enforce_known_alignment = indirect_alignment;
// llvm::getOrEnforceKnownAlignment(Addr.emitRawPointer(*this),
// Align.getAsAlign(),
// *TD) < Align.getAsAlign()) {
// TODO
const need_copy = switch (address_alignment < indirect_alignment and get_or_enforce_known_alignment < indirect_alignment) {
true => @trap(),
false => b: {
const is_lvalue = !(semantic_argument_value.type.?.bb == .pointer and semantic_argument_type == semantic_argument_value.type.?.bb.pointer.type);
if (is_lvalue) {
var need_copy = false;
const is_by_val_or_by_ref = argument_abi.flags.kind == .indirect_aliased or argument_abi.flags.indirect_by_value;
const lv_alignment = semantic_argument_value.type.?.get_byte_alignment();
const arg_type_alignment = argument_abi.semantic_type.get_byte_alignment();
if (!is_by_val_or_by_ref or lv_alignment < arg_type_alignment) {
need_copy = true;
}
break :b need_copy;
} else {
break :b false;
}
},
};
if (!need_copy) {
llvm_abi_argument_value_buffer[abi_argument_count] = semantic_argument_value.llvm.?;
abi_argument_count += 1;
break :indirect;
}
}
@trap();
},
.ignore => unreachable,
else => @trap(),
}
assert(abi_argument_count == argument_abi.abi_start + argument_abi.abi_count);
}
if (function_type.is_var_args) {
assert(abi_argument_count >= function_type.abi_argument_types.len);
} else {
// TODO
assert(abi_argument_count == function_type.abi_argument_types.len);
}
const llvm_abi_argument_values = llvm_abi_argument_value_buffer[0..abi_argument_count];
const llvm_call = module.llvm.builder.create_call(raw_function_type.llvm.handle.?.to_function(), llvm_callable, llvm_abi_argument_values);
const attribute_list = module.build_attribute_list(.{
.return_type_abi = function_type.return_abi,
.abi_return_type = function_type.abi_return_type,
.abi_argument_types = abi_argument_type_buffer[0..abi_argument_count],
.argument_type_abis = argument_type_abi_buffer[0..call.arguments.len],
.attributes = .{},
.call_site = true,
});
const call_base = llvm_call.to_instruction().to_call_base();
call_base.set_calling_convention(llvm_calling_convention);
call_base.set_attributes(attribute_list);
const return_type_abi = &function_type.return_abi;
const return_abi_kind = return_type_abi.flags.kind;
switch (return_abi_kind) {
.ignore => {
assert(return_type_abi.semantic_type == module.noreturn_type or return_type_abi.semantic_type == module.void_type);
break :c llvm_call;
},
.direct, .extend => {
const coerce_to_type = return_type_abi.get_coerce_to_type();
if (return_type_abi.semantic_type.is_abi_equal(coerce_to_type) and return_type_abi.attributes.direct.offset == 0) {
const coerce_to_type_kind = coerce_to_type.get_evaluation_kind();
switch (coerce_to_type_kind) {
.aggregate => {},
.complex => @trap(),
.scalar => {
break :c llvm_call;
// TODO: maybe a bug?
// const v = module.values.add();
// v.* = .{
// .llvm = llvm_call,
// .bb = .instruction,
// .type = return_type_abi.semantic_type,
// .lvalue = false,
// .dereference_to_assign = false,
// };
// break :c v;
},
}
}
// TODO: if
const fixed_vector_type = false;
if (fixed_vector_type) {
@trap();
}
const coerce_alloca = module.create_alloca(.{ .type = return_type_abi.semantic_type, .name = "coerce" });
var destination_pointer = switch (return_type_abi.attributes.direct.offset == 0) {
true => coerce_alloca,
false => @trap(),
};
_ = &destination_pointer;
if (return_type_abi.semantic_type.bb.structure.fields.len > 0) {
// CreateCoercedStore(
// CI, StorePtr,
// llvm::TypeSize::getFixed(DestSize - RetAI.getDirectOffset()),
// DestIsVolatile);
// const source_value = llvm_call;
// const source_type = function_type.abi_return_type;
// // const source_size = source_type.get_byte_size();
// var destination_type = return_type_abi.semantic_type;
// const destination_size = destination_type.get_byte_size();
// // const destination_alignment = destination_type.get_byte_alignment();
// const left_destination_size = destination_size - return_type_abi.attributes.direct.offset;
//
// const is_destination_volatile = false; // TODO
// module.create_coerced_store(source_value, source_type, destination_pointer, destination_type, left_destination_size, is_destination_volatile);
// TODO:
@trap();
} else {
@trap();
}
const v = module.values.add();
v.* = .{
.llvm = destination_pointer,
.bb = .instruction,
.type = module.get_pointer_type(.{ .type = return_type_abi.semantic_type }),
.lvalue = true,
.dereference_to_assign = true,
};
break :c v;
},
.indirect => {
@trap();
// TODO
// const v = module.values.add();
// v.* = .{
// .llvm = llvm_indirect_return_value,
// .bb = .instruction,
// .type = module.get_pointer_type(.{ .type = return_type_abi.semantic_type }),
// .lvalue = true,
// .dereference_to_assign = true,
// };
// break :c v;
},
else => @trap(),
}
},
else => @trap(), else => @trap(),
}; };
@ -2906,7 +3364,10 @@ pub const Module = struct {
assert(expected_type == unary.value.type); assert(expected_type == unary.value.type);
}, },
.@"&" => @trap(), .@"&" => {
module.analyze_value_type(function, unary.value, analysis);
assert(expected_type == unary.value.type);
},
} }
}, },
.binary => |binary| { .binary => |binary| {
@ -2926,9 +3387,16 @@ pub const Module = struct {
.type = binary.left.type, .type = binary.left.type,
}); });
}, },
.variable_reference => |variable| { .variable_reference => |variable| switch (value.kind) {
if (variable.type != expected_type) { .left => {
module.report_error(); if (variable.type != expected_type.bb.pointer.type) {
module.report_error();
}
},
.right => {
if (variable.type != expected_type) {
module.report_error();
}
} }
}, },
.intrinsic => |intrinsic| switch (intrinsic) { .intrinsic => |intrinsic| switch (intrinsic) {
@ -2962,10 +3430,21 @@ pub const Module = struct {
module.report_error(); module.report_error();
} }
}, },
.call => |call| { .call => |*call| {
module.analyze_value_type(function, call.callable, .{}); module.analyze_value_type(function, call.callable, .{});
for (call.arguments) |argument| { call.function_type = switch (call.callable.type.?.bb) {
module.analyze_value_type(function, argument, .{}); .function => call.callable.type.?,
else => @trap(),
};
if (call.arguments.len != call.function_type.bb.function.semantic_argument_types.len) {
module.report_error();
}
for (call.arguments, call.function_type.bb.function.semantic_argument_types) |argument, argument_type| {
module.analyze_value_type(function, argument, .{ .type = argument_type, });
}
if (call.function_type.bb.function.semantic_return_type != expected_type) {
module.report_error();
} }
}, },
else => @trap(), else => @trap(),
@ -2997,10 +3476,12 @@ pub const Module = struct {
module.analyze_value_type(function, binary.right, .{}); module.analyze_value_type(function, binary.right, .{});
} }
const is_boolean = binary.id.is_boolean();
assert(binary.left.type != null); assert(binary.left.type != null);
assert(binary.right.type != null); assert(binary.right.type != null);
assert(binary.left.type == binary.right.type); assert(binary.left.type == binary.right.type);
break :blk binary.left.type.?; break :blk if (is_boolean) module.integer_type(1, false) else binary.left.type.?;
}, },
.variable_reference => |variable| switch (value.kind) { .variable_reference => |variable| switch (value.kind) {
.right => variable.type, .right => variable.type,
@ -3016,6 +3497,35 @@ pub const Module = struct {
}, },
else => @trap(), else => @trap(),
}, },
.dereference => |dereferenced_value| blk: {
module.analyze_value_type(function, dereferenced_value, .{});
const dereference_type = switch (value.kind) {
.left => @trap(),
.right => dereferenced_value.type.?.bb.pointer.type,
};
break :blk dereference_type;
},
.call => |*call| blk: {
module.analyze_value_type(function, call.callable, .{});
call.function_type = switch (call.callable.type.?.bb) {
.pointer => |pointer| switch (pointer.type.bb) {
.function => pointer.type,
else => @trap(),
},
else => @trap(),
};
const argument_types = call.function_type.bb.function.semantic_argument_types;
if (argument_types.len != call.arguments.len) {
module.report_error();
}
for (argument_types, call.arguments) |argument_type, call_argument| {
module.analyze_value_type(function, call_argument, .{ .type = argument_type });
}
break :blk call.function_type.bb.function.semantic_return_type;
},
else => @trap(), else => @trap(),
}; };
@ -3162,6 +3672,18 @@ pub const Module = struct {
module.emit_assignment(function, local.variable.storage.?, local.variable.initial_value); module.emit_assignment(function, local.variable.storage.?, local.variable.initial_value);
}, },
.assignment => |assignment| {
switch (assignment.kind) {
.@"=" => {
module.analyze(function, assignment.left, .{});
module.analyze_value_type(function, assignment.right, .{ .type = assignment.left.type.?.bb.pointer.type });
module.emit_assignment(function, assignment.left, assignment.right);
},
}
},
.expression => |expression_value| {
module.analyze(function, expression_value, .{});
},
} }
} }
} }
@ -3530,13 +4052,16 @@ pub const Abi = struct {
can_be_flattened: bool = true, can_be_flattened: bool = true,
}; };
pub fn get_direct(direct: Direct) Information { pub fn get_direct(module: *Module, direct: Direct) Information {
var result = Information{ var result = Information{
.semantic_type = direct.semantic_type, .semantic_type = direct.semantic_type,
.flags = .{ .flags = .{
.kind = .direct, .kind = .direct,
}, },
}; };
_ = direct.semantic_type.resolve(module);
_ = direct.semantic_type.resolve(module);
if (direct.padding) |p| _ = p.resolve(module);
result.set_coerce_to_type(direct.type); result.set_coerce_to_type(direct.type);
result.set_padding_type(direct.padding); result.set_padding_type(direct.padding);
result.set_direct_offset(direct.offset); result.set_direct_offset(direct.offset);
@ -3549,7 +4074,8 @@ pub const Abi = struct {
semantic_type: *Type, semantic_type: *Type,
}; };
pub fn get_ignore(ignore: Ignore) Information { pub fn get_ignore(module: *Module, ignore: Ignore) Information {
_ = ignore.semantic_type.resolve(module);
return Information{ return Information{
.semantic_type = ignore.semantic_type, .semantic_type = ignore.semantic_type,
.flags = .{ .flags = .{
@ -4067,7 +4593,7 @@ pub const Abi = struct {
}, },
.memory, .x87, .complex_x87 => { .memory, .x87, .complex_x87 => {
// TODO: CXX ABI: RAA_Indirect // TODO: CXX ABI: RAA_Indirect
return .{ get_indirect_result(argument_type, options.available_gpr), needed_registers }; return .{ get_indirect_result(module, argument_type, options.available_gpr), needed_registers };
}, },
else => @trap(), else => @trap(),
} }
@ -4089,7 +4615,7 @@ pub const Abi = struct {
const result_type = if (high) |hi| get_by_val_argument_pair(module, low orelse unreachable, hi) else low orelse unreachable; const result_type = if (high) |hi| get_by_val_argument_pair(module, low orelse unreachable, hi) else low orelse unreachable;
return .{ return .{
Abi.Information.get_direct(.{ Abi.Information.get_direct(module, .{
.semantic_type = argument_type, .semantic_type = argument_type,
.type = result_type, .type = result_type,
}), }),
@ -4120,7 +4646,7 @@ pub const Abi = struct {
available_registers.system_v.sse -= needed_registers.sse; available_registers.system_v.sse -= needed_registers.sse;
break :blk abi; break :blk abi;
}, },
false => Abi.SystemV.get_indirect_result(semantic_argument_type, available_registers.system_v.gpr), false => Abi.SystemV.get_indirect_result(module, semantic_argument_type, available_registers.system_v.gpr),
}; };
if (argument_type_abi.get_padding_type() != null) { if (argument_type_abi.get_padding_type() != null) {
@ -4200,7 +4726,7 @@ pub const Abi = struct {
switch (classes[0]) { switch (classes[0]) {
.none => { .none => {
if (classes[1] == .none) { if (classes[1] == .none) {
return Abi.Information.get_ignore(.{ return Abi.Information.get_ignore(module, .{
.semantic_type = return_type, .semantic_type = return_type,
}); });
} }
@ -4239,7 +4765,7 @@ pub const Abi = struct {
const high_ty = Abi.SystemV.get_int_type_at_offset(module, return_type, high_offset, return_type, high_offset); const high_ty = Abi.SystemV.get_int_type_at_offset(module, return_type, high_offset, return_type, high_offset);
high = high_ty; high = high_ty;
if (classes[0] == .none) { if (classes[0] == .none) {
return Abi.Information.get_direct(.{ return Abi.Information.get_direct(module, .{
.semantic_type = return_type, .semantic_type = return_type,
.type = high_ty, .type = high_ty,
.offset = high_offset, .offset = high_offset,
@ -4253,7 +4779,7 @@ pub const Abi = struct {
low = Abi.SystemV.get_byval_argument_pair(module, .{ low orelse unreachable, hi }); low = Abi.SystemV.get_byval_argument_pair(module, .{ low orelse unreachable, hi });
} }
return Abi.Information.get_direct(.{ return Abi.Information.get_direct(module, .{
.semantic_type = return_type, .semantic_type = return_type,
.type = low orelse unreachable, .type = low orelse unreachable,
}); });
@ -4296,11 +4822,11 @@ pub const Abi = struct {
} }
} }
pub fn get_indirect_result(ty: *Type, free_gpr: u32) Abi.Information { pub fn get_indirect_result(module: *Module, ty: *Type, free_gpr: u32) Abi.Information {
if (!ty.is_aggregate_type_for_abi() and !is_illegal_vector_type(ty) and !ty.is_arbitrary_bit_integer()) { if (!ty.is_aggregate_type_for_abi() and !is_illegal_vector_type(ty) and !ty.is_arbitrary_bit_integer()) {
return switch (ty.is_promotable_integer_type_for_abi()) { return switch (ty.is_promotable_integer_type_for_abi()) {
true => @trap(), true => @trap(),
false => Abi.Information.get_direct(.{ false => Abi.Information.get_direct(module, .{
.semantic_type = ty, .semantic_type = ty,
.type = ty, .type = ty,
}), }),
@ -4402,7 +4928,12 @@ pub fn compile(arena: *Arena, options: Options) void {
}); });
const globals = Global.Buffer.initialize(); const globals = Global.Buffer.initialize();
const values = Value.Buffer.initialize(); var values = Value.Buffer.initialize();
const void_value = values.add();
void_value.* = .{
.bb = .infer_or_ignore,
.type = void_type,
};
var module = Module{ var module = Module{
.arena = arena, .arena = arena,
@ -4420,6 +4951,7 @@ pub fn compile(arena: *Arena, options: Options) void {
.statements = .initialize(), .statements = .initialize(),
.void_type = void_type, .void_type = void_type,
.noreturn_type = noreturn_type, .noreturn_type = noreturn_type,
.void_value = void_value,
.current_scope = undefined, .current_scope = undefined,
.scope = .{ .scope = .{
.kind = .global, .kind = .global,

View File

@ -193,5 +193,5 @@ const names = &[_][]const u8{
"integer_hex", "integer_hex",
"basic_pointer", "basic_pointer",
"basic_call", "basic_call",
// "pointer", "pointer",
}; };