Minimal stack #3
@ -98,10 +98,18 @@ fn is_identifier_ch(ch: u8) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const Variable = struct {
|
pub const Variable = struct {
|
||||||
value: *Value,
|
storage: ?*Value = null,
|
||||||
|
initial_value: *Value,
|
||||||
|
type: ?*Type,
|
||||||
|
scope: *Scope,
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
line: u32,
|
line: u32,
|
||||||
column: u32,
|
column: u32,
|
||||||
|
|
||||||
|
fn resolve_type(variable: *Variable, auxiliary_type: *Type) void {
|
||||||
|
const resolved_type: *Type = variable.type orelse auxiliary_type;
|
||||||
|
variable.type = resolved_type;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Local = struct {
|
pub const Local = struct {
|
||||||
@ -131,6 +139,10 @@ pub const Local = struct {
|
|||||||
pub fn get_slice(locals: *Buffer) []Local {
|
pub fn get_slice(locals: *Buffer) []Local {
|
||||||
return locals.buffer.get_slice();
|
return locals.buffer.get_slice();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add(locals: *Buffer) *Local {
|
||||||
|
return locals.buffer.add();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -296,17 +308,19 @@ pub const Type = struct {
|
|||||||
pub fn get_byte_alignment(ty: *const Type) u32 {
|
pub fn get_byte_alignment(ty: *const Type) u32 {
|
||||||
const result: u32 = switch (ty.bb) {
|
const result: u32 = switch (ty.bb) {
|
||||||
.void => unreachable,
|
.void => unreachable,
|
||||||
.integer => |integer| @intCast(@max(8, lib.next_power_of_two(integer.bit_count))),
|
.integer => |integer| @intCast(@min(@divExact(@max(8, lib.next_power_of_two(integer.bit_count)), 8), 16)),
|
||||||
.pointer => 8,
|
.pointer => 8,
|
||||||
.function => 1,
|
.function => 1,
|
||||||
else => @trap(),
|
else => @trap(),
|
||||||
};
|
};
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_bit_alignment(ty: *const Type) u32 {
|
pub fn get_bit_alignment(ty: *const Type) u32 {
|
||||||
_ = ty;
|
_ = ty;
|
||||||
@trap();
|
@trap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_byte_size(ty: *const Type) u64 {
|
pub fn get_byte_size(ty: *const Type) u64 {
|
||||||
const byte_size: u64 = switch (ty.bb) {
|
const byte_size: u64 = switch (ty.bb) {
|
||||||
.integer => |integer| @max(8, lib.next_power_of_two(integer.bit_count)),
|
.integer => |integer| @max(8, lib.next_power_of_two(integer.bit_count)),
|
||||||
@ -355,6 +369,7 @@ const ConstantInteger = struct {
|
|||||||
|
|
||||||
pub const Statement = struct {
|
pub const Statement = struct {
|
||||||
bb: union(enum) {
|
bb: union(enum) {
|
||||||
|
local: *Local,
|
||||||
@"return": ?*Value,
|
@"return": ?*Value,
|
||||||
},
|
},
|
||||||
line: u32,
|
line: u32,
|
||||||
@ -403,6 +418,8 @@ pub const Value = struct {
|
|||||||
constant_integer: ConstantInteger,
|
constant_integer: ConstantInteger,
|
||||||
unary: Unary,
|
unary: Unary,
|
||||||
binary: Binary,
|
binary: Binary,
|
||||||
|
variable_reference: *Variable,
|
||||||
|
local,
|
||||||
},
|
},
|
||||||
type: ?*Type = null,
|
type: ?*Type = null,
|
||||||
llvm: ?*llvm.Value = null,
|
llvm: ?*llvm.Value = null,
|
||||||
@ -557,7 +574,7 @@ pub const Scope = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const LexicalBlock = struct {
|
pub const LexicalBlock = struct {
|
||||||
locals: Local.Buffer,
|
locals: lib.VirtualBuffer(*Local),
|
||||||
statements: lib.VirtualBuffer(*Statement),
|
statements: lib.VirtualBuffer(*Statement),
|
||||||
scope: Scope,
|
scope: Scope,
|
||||||
};
|
};
|
||||||
@ -603,6 +620,7 @@ pub const Module = struct {
|
|||||||
line_offset: u64,
|
line_offset: u64,
|
||||||
line_character_offset: u64,
|
line_character_offset: u64,
|
||||||
types: Type.Buffer,
|
types: Type.Buffer,
|
||||||
|
locals: Local.Buffer,
|
||||||
globals: Global.Buffer,
|
globals: Global.Buffer,
|
||||||
values: Value.Buffer,
|
values: Value.Buffer,
|
||||||
pointer_types: IndexBuffer,
|
pointer_types: IndexBuffer,
|
||||||
@ -913,7 +931,6 @@ pub const Module = struct {
|
|||||||
},
|
},
|
||||||
else => @trap(),
|
else => @trap(),
|
||||||
}
|
}
|
||||||
@trap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume_character_if_match(noalias module: *Module, expected_ch: u8) bool {
|
fn consume_character_if_match(noalias module: *Module, expected_ch: u8) bool {
|
||||||
@ -1501,8 +1518,36 @@ pub const Module = struct {
|
|||||||
const require_semicolon = true;
|
const require_semicolon = true;
|
||||||
statement.* = .{
|
statement.* = .{
|
||||||
.bb = switch (statement_start_character) {
|
.bb = switch (statement_start_character) {
|
||||||
'>' => {
|
'>' => blk: {
|
||||||
@trap();
|
module.offset += 1;
|
||||||
|
module.skip_space();
|
||||||
|
const local_name = module.parse_identifier();
|
||||||
|
module.skip_space();
|
||||||
|
const local_type: ?*Type = if (module.consume_character_if_match(':')) b: {
|
||||||
|
module.skip_space();
|
||||||
|
const t = module.parse_type();
|
||||||
|
module.skip_space();
|
||||||
|
break :b t;
|
||||||
|
} else null;
|
||||||
|
module.expect_character('=');
|
||||||
|
const local_value = module.parse_value(.{});
|
||||||
|
const local = module.locals.add();
|
||||||
|
local.* = .{
|
||||||
|
.variable = .{
|
||||||
|
.initial_value = local_value,
|
||||||
|
.type = local_type,
|
||||||
|
.name = local_name,
|
||||||
|
.line = statement_line,
|
||||||
|
.column = statement_column,
|
||||||
|
.scope = module.current_scope,
|
||||||
|
},
|
||||||
|
.is_argument = false,
|
||||||
|
};
|
||||||
|
assert(module.current_scope == &block.scope);
|
||||||
|
_ = block.locals.append(local);
|
||||||
|
break :blk .{
|
||||||
|
.local = local,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
'#' => {
|
'#' => {
|
||||||
@trap();
|
@trap();
|
||||||
@ -1543,9 +1588,38 @@ pub const Module = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn rule_before_identifier(noalias module: *Module, value_builder: Value.Builder) *Value {
|
fn rule_before_identifier(noalias module: *Module, value_builder: Value.Builder) *Value {
|
||||||
_ = module;
|
const identifier = value_builder.token.identifier;
|
||||||
_ = value_builder;
|
assert(!lib.string.equal(identifier, ""));
|
||||||
|
assert(!lib.string.equal(identifier, "_"));
|
||||||
|
|
||||||
|
var scope_it: ?*Scope = module.current_scope;
|
||||||
|
const variable = blk: while (scope_it) |scope| : (scope_it = scope.parent) {
|
||||||
|
switch (scope.kind) {
|
||||||
|
.global => {
|
||||||
@trap();
|
@trap();
|
||||||
|
},
|
||||||
|
.function => {
|
||||||
|
@trap();
|
||||||
|
},
|
||||||
|
.local => {
|
||||||
|
const block: *LexicalBlock = @fieldParentPtr("scope", scope);
|
||||||
|
for (block.locals.get_slice()) |local| {
|
||||||
|
if (lib.string.equal(local.variable.name, identifier)) {
|
||||||
|
break :blk &local.variable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
module.report_error();
|
||||||
|
};
|
||||||
|
const value = module.values.add();
|
||||||
|
value.* = .{
|
||||||
|
.bb = .{
|
||||||
|
.variable_reference = variable,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rule_before_value_keyword(noalias module: *Module, value_builder: Value.Builder) *Value {
|
fn rule_before_value_keyword(noalias module: *Module, value_builder: Value.Builder) *Value {
|
||||||
@ -1802,11 +1876,7 @@ pub const Module = struct {
|
|||||||
|
|
||||||
const is_declaration = module.consume_character_if_match(';');
|
const is_declaration = module.consume_character_if_match(';');
|
||||||
|
|
||||||
const value = module.values.add();
|
const function_type = module.types.append(.{
|
||||||
value.* = .{
|
|
||||||
.bb = undefined,
|
|
||||||
.type = module.get_pointer_type(.{
|
|
||||||
.type = module.types.append(.{
|
|
||||||
.bb = .{
|
.bb = .{
|
||||||
.function = .{
|
.function = .{
|
||||||
.semantic_return_type = return_type,
|
.semantic_return_type = return_type,
|
||||||
@ -1816,17 +1886,25 @@ pub const Module = struct {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
.name = "",
|
.name = "",
|
||||||
}),
|
});
|
||||||
|
const storage = module.values.add();
|
||||||
|
storage.* = .{
|
||||||
|
.bb = undefined,
|
||||||
|
.type = module.get_pointer_type(.{
|
||||||
|
.type = function_type,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
const global = module.globals.add();
|
const global = module.globals.add();
|
||||||
global.* = .{
|
global.* = .{
|
||||||
.variable = .{
|
.variable = .{
|
||||||
.value = value,
|
.storage = storage,
|
||||||
|
.initial_value = undefined,
|
||||||
|
.type = function_type,
|
||||||
.name = global_name,
|
.name = global_name,
|
||||||
.line = global_line,
|
.line = global_line,
|
||||||
.column = global_column,
|
.column = global_column,
|
||||||
|
.scope = &module.scope,
|
||||||
},
|
},
|
||||||
.linkage = if (is_export or is_extern) .external else .internal,
|
.linkage = if (is_export or is_extern) .external else .internal,
|
||||||
};
|
};
|
||||||
@ -1834,7 +1912,7 @@ pub const Module = struct {
|
|||||||
defer module.current_function = null;
|
defer module.current_function = null;
|
||||||
|
|
||||||
if (!is_declaration) {
|
if (!is_declaration) {
|
||||||
value.bb = .{
|
storage.bb = .{
|
||||||
.function = .{
|
.function = .{
|
||||||
.main_block = undefined,
|
.main_block = undefined,
|
||||||
.arguments = .initialize(),
|
.arguments = .initialize(),
|
||||||
@ -1848,10 +1926,10 @@ pub const Module = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const global_scope = module.current_scope;
|
const global_scope = module.current_scope;
|
||||||
module.current_scope = &value.bb.function.scope;
|
module.current_scope = &storage.bb.function.scope;
|
||||||
defer module.current_scope = global_scope;
|
defer module.current_scope = global_scope;
|
||||||
|
|
||||||
value.bb.function.main_block = module.parse_block();
|
storage.bb.function.main_block = module.parse_block();
|
||||||
} else {
|
} else {
|
||||||
// TODO: initialize value.bb
|
// TODO: initialize value.bb
|
||||||
@trap();
|
@trap();
|
||||||
@ -1909,7 +1987,7 @@ pub const Module = struct {
|
|||||||
if (maybe_current_block != null and maybe_current_block.?.get_parent() != null) {
|
if (maybe_current_block != null and maybe_current_block.?.get_parent() != null) {
|
||||||
module.llvm.builder.insert_basic_block_after_insert_block(block);
|
module.llvm.builder.insert_basic_block_after_insert_block(block);
|
||||||
} else {
|
} else {
|
||||||
function.variable.value.llvm.?.to_function().append_basic_block(block);
|
function.variable.storage.?.llvm.?.to_function().append_basic_block(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.llvm.builder.position_at_end(block);
|
module.llvm.builder.position_at_end(block);
|
||||||
@ -1963,19 +2041,21 @@ pub const Module = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (module.globals.get_slice()) |*global| {
|
for (module.globals.get_slice()) |*global| {
|
||||||
const global_type = global.variable.value.type.?.bb.pointer.type;
|
switch (global.variable.storage.?.bb) {
|
||||||
|
.function => {
|
||||||
|
const function_type = &global.variable.storage.?.type.?.bb.pointer.type.bb.function;
|
||||||
|
const argument_variables = global.variable.storage.?.bb.function.arguments.get_slice();
|
||||||
|
function_type.argument_abis = module.arena.allocate(Abi.Information, argument_variables.len);
|
||||||
|
|
||||||
switch (global_type.bb) {
|
const resolved_calling_convention = function_type.calling_convention.resolve(module.target);
|
||||||
.function => |*function| {
|
|
||||||
const argument_variables = global.variable.value.bb.function.arguments.get_slice();
|
|
||||||
function.argument_abis = module.arena.allocate(Abi.Information, argument_variables.len);
|
|
||||||
|
|
||||||
const resolved_calling_convention = function.calling_convention.resolve(module.target);
|
|
||||||
const is_reg_call = resolved_calling_convention == .system_v and false; // TODO: regcall calling_convention
|
const is_reg_call = resolved_calling_convention == .system_v and false; // TODO: regcall calling_convention
|
||||||
|
|
||||||
|
var llvm_abi_argument_type_buffer: [64]*llvm.Type = undefined;
|
||||||
|
var abi_argument_type_buffer: [64]*Type = undefined;
|
||||||
|
var abi_argument_type_count: u16 = 0;
|
||||||
switch (resolved_calling_convention) {
|
switch (resolved_calling_convention) {
|
||||||
.system_v => {
|
.system_v => {
|
||||||
function.available_registers = switch (resolved_calling_convention) {
|
function_type.available_registers = switch (resolved_calling_convention) {
|
||||||
.system_v => .{
|
.system_v => .{
|
||||||
.system_v = .{
|
.system_v = .{
|
||||||
.gpr = if (is_reg_call) 11 else 6,
|
.gpr = if (is_reg_call) 11 else 6,
|
||||||
@ -1984,34 +2064,31 @@ pub const Module = struct {
|
|||||||
},
|
},
|
||||||
.win64 => @trap(),
|
.win64 => @trap(),
|
||||||
};
|
};
|
||||||
var abi_argument_type_count: u16 = 0;
|
|
||||||
var llvm_abi_argument_type_buffer: [64]*llvm.Type = undefined;
|
|
||||||
var abi_argument_type_buffer: [64]*Type = undefined;
|
|
||||||
|
|
||||||
function.return_abi = Abi.SystemV.classify_return_type(module, function.semantic_return_type);
|
function_type.return_abi = Abi.SystemV.classify_return_type(module, function_type.semantic_return_type);
|
||||||
const return_abi_kind = function.return_abi.flags.kind;
|
const return_abi_kind = function_type.return_abi.flags.kind;
|
||||||
function.abi_return_type = switch (return_abi_kind) {
|
function_type.abi_return_type = switch (return_abi_kind) {
|
||||||
.direct, .extend => function.return_abi.coerce_to_type.?,
|
.direct, .extend => function_type.return_abi.coerce_to_type.?,
|
||||||
.ignore, .indirect => module.void_type,
|
.ignore, .indirect => module.void_type,
|
||||||
else => |t| @panic(@tagName(t)),
|
else => |t| @panic(@tagName(t)),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (function.return_abi.flags.kind == .indirect) {
|
if (function_type.return_abi.flags.kind == .indirect) {
|
||||||
assert(!function.return_abi.flags.sret_after_this);
|
assert(!function_type.return_abi.flags.sret_after_this);
|
||||||
function.available_registers.system_v.gpr -= 1;
|
function_type.available_registers.system_v.gpr -= 1;
|
||||||
const indirect_type = module.get_pointer_type(.{ .type = function.return_abi.semantic_type });
|
const indirect_type = module.get_pointer_type(.{ .type = function_type.return_abi.semantic_type });
|
||||||
abi_argument_type_buffer[abi_argument_type_count] = indirect_type;
|
abi_argument_type_buffer[abi_argument_type_count] = indirect_type;
|
||||||
llvm_abi_argument_type_buffer[abi_argument_type_count] = indirect_type.llvm.handle.?;
|
llvm_abi_argument_type_buffer[abi_argument_type_count] = indirect_type.llvm.handle.?;
|
||||||
abi_argument_type_count += 1;
|
abi_argument_type_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const required_arguments = function.semantic_argument_types.len;
|
const required_arguments = function_type.semantic_argument_types.len;
|
||||||
|
|
||||||
for (function.argument_abis, function.semantic_argument_types, 0..) |*argument_type_abi, semantic_argument_type, semantic_argument_index| {
|
for (function_type.argument_abis, function_type.semantic_argument_types, 0..) |*argument_type_abi, semantic_argument_type, semantic_argument_index| {
|
||||||
const is_named_argument = semantic_argument_index < required_arguments;
|
const is_named_argument = semantic_argument_index < required_arguments;
|
||||||
assert(is_named_argument);
|
assert(is_named_argument);
|
||||||
|
|
||||||
argument_type_abi.* = Abi.SystemV.classify_argument(module, &function.available_registers, &llvm_abi_argument_type_buffer, &abi_argument_type_buffer, .{
|
argument_type_abi.* = Abi.SystemV.classify_argument(module, &function_type.available_registers, &llvm_abi_argument_type_buffer, &abi_argument_type_buffer, .{
|
||||||
.type = semantic_argument_type,
|
.type = semantic_argument_type,
|
||||||
.abi_start = abi_argument_type_count,
|
.abi_start = abi_argument_type_count,
|
||||||
.is_named_argument = is_named_argument,
|
.is_named_argument = is_named_argument,
|
||||||
@ -2020,61 +2097,60 @@ pub const Module = struct {
|
|||||||
abi_argument_type_count += argument_type_abi.abi_count;
|
abi_argument_type_count += argument_type_abi.abi_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
function.abi_argument_types = module.arena.allocate(*Type, abi_argument_type_count);
|
function_type.abi_argument_types = module.arena.allocate(*Type, abi_argument_type_count);
|
||||||
@memcpy(function.abi_argument_types, abi_argument_type_buffer[0..function.abi_argument_types.len]);
|
@memcpy(function_type.abi_argument_types, abi_argument_type_buffer[0..function_type.abi_argument_types.len]);
|
||||||
|
|
||||||
const llvm_abi_argument_types = llvm_abi_argument_type_buffer[0..abi_argument_type_count];
|
|
||||||
const llvm_function_type = llvm.Type.Function.get(function.abi_return_type.llvm.handle.?, llvm_abi_argument_types, function.is_var_args);
|
|
||||||
|
|
||||||
const subroutine_type_flags = llvm.DI.Flags{};
|
|
||||||
const subroutine_type = if (module.has_debug_info) blk: {
|
|
||||||
var debug_argument_type_buffer: [64 + 1]*llvm.DI.Type = undefined;
|
|
||||||
const semantic_debug_argument_types = debug_argument_type_buffer[0 .. function.argument_abis.len + 1 + @intFromBool(function.is_var_args)];
|
|
||||||
semantic_debug_argument_types[0] = function.return_abi.semantic_type.llvm.debug.?;
|
|
||||||
|
|
||||||
for (function.argument_abis, semantic_debug_argument_types[1..][0..function.argument_abis.len]) |argument_abi, *debug_argument_type| {
|
|
||||||
debug_argument_type.* = argument_abi.semantic_type.llvm.debug.?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (function.is_var_args) {
|
|
||||||
semantic_debug_argument_types[function.argument_abis.len + 1] = module.void_type.llvm.debug.?;
|
|
||||||
}
|
|
||||||
|
|
||||||
const subroutine_type = module.llvm.di_builder.create_subroutine_type(module.llvm.file, semantic_debug_argument_types, subroutine_type_flags);
|
|
||||||
break :blk subroutine_type;
|
|
||||||
} else undefined;
|
|
||||||
|
|
||||||
global_type.llvm.handle = llvm_function_type.to_type();
|
|
||||||
global_type.llvm.debug = subroutine_type.to_type();
|
|
||||||
},
|
},
|
||||||
.win64 => {
|
.win64 => {
|
||||||
@trap();
|
@trap();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
const llvm_abi_argument_types = llvm_abi_argument_type_buffer[0..abi_argument_type_count];
|
||||||
|
const llvm_function_type = llvm.Type.Function.get(function_type.abi_return_type.llvm.handle.?, llvm_abi_argument_types, function_type.is_var_args);
|
||||||
|
|
||||||
const function_value = module.llvm.module.create_function(.{
|
const subroutine_type_flags = llvm.DI.Flags{};
|
||||||
|
const subroutine_type = if (module.has_debug_info) blk: {
|
||||||
|
var debug_argument_type_buffer: [64 + 1]*llvm.DI.Type = undefined;
|
||||||
|
const semantic_debug_argument_types = debug_argument_type_buffer[0 .. function_type.argument_abis.len + 1 + @intFromBool(function_type.is_var_args)];
|
||||||
|
semantic_debug_argument_types[0] = function_type.return_abi.semantic_type.llvm.debug.?;
|
||||||
|
|
||||||
|
for (function_type.argument_abis, semantic_debug_argument_types[1..][0..function_type.argument_abis.len]) |argument_abi, *debug_argument_type| {
|
||||||
|
debug_argument_type.* = argument_abi.semantic_type.llvm.debug.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (function_type.is_var_args) {
|
||||||
|
semantic_debug_argument_types[function_type.argument_abis.len + 1] = module.void_type.llvm.debug.?;
|
||||||
|
}
|
||||||
|
|
||||||
|
const subroutine_type = module.llvm.di_builder.create_subroutine_type(module.llvm.file, semantic_debug_argument_types, subroutine_type_flags);
|
||||||
|
break :blk subroutine_type;
|
||||||
|
} else undefined;
|
||||||
|
global.variable.storage.?.type.?.bb.pointer.type.llvm.handle = llvm_function_type.to_type();
|
||||||
|
global.variable.storage.?.type.?.bb.pointer.type.llvm.debug = subroutine_type.to_type();
|
||||||
|
|
||||||
|
const llvm_function_value = module.llvm.module.create_function(.{
|
||||||
.name = global.variable.name,
|
.name = global.variable.name,
|
||||||
// TODO: make it better
|
// TODO: make it better
|
||||||
.linkage = switch (global.linkage) {
|
.linkage = switch (global.linkage) {
|
||||||
.external => .ExternalLinkage,
|
.external => .ExternalLinkage,
|
||||||
.internal => .InternalLinkage,
|
.internal => .InternalLinkage,
|
||||||
},
|
},
|
||||||
.type = global_type.llvm.handle.?.to_function(),
|
.type = global.variable.storage.?.type.?.bb.pointer.type.llvm.handle.?.to_function(),
|
||||||
});
|
});
|
||||||
global.variable.value.llvm = function_value.to_value();
|
|
||||||
|
|
||||||
function_value.set_calling_convention(function.calling_convention.to_llvm());
|
global.variable.storage.?.llvm = llvm_function_value.to_value();
|
||||||
|
|
||||||
|
llvm_function_value.set_calling_convention(function_type.calling_convention.to_llvm());
|
||||||
|
|
||||||
const attribute_list = module.build_attribute_list(.{
|
const attribute_list = module.build_attribute_list(.{
|
||||||
.abi_return_type = function.abi_return_type,
|
.abi_return_type = function_type.abi_return_type,
|
||||||
.abi_argument_types = function.abi_argument_types,
|
.abi_argument_types = function_type.abi_argument_types,
|
||||||
.argument_type_abis = function.argument_abis,
|
.argument_type_abis = function_type.argument_abis,
|
||||||
.return_type_abi = function.return_abi,
|
.return_type_abi = function_type.return_abi,
|
||||||
.attributes = global.variable.value.bb.function.attributes,
|
.attributes = global.variable.storage.?.bb.function.attributes,
|
||||||
.call_site = false,
|
.call_site = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
function_value.set_attributes(attribute_list);
|
llvm_function_value.set_attributes(attribute_list);
|
||||||
|
|
||||||
const function_scope: *llvm.DI.Scope = if (module.has_debug_info) blk: {
|
const function_scope: *llvm.DI.Scope = if (module.has_debug_info) blk: {
|
||||||
const scope_line: u32 = @intCast(module.line_offset + 1);
|
const scope_line: u32 = @intCast(module.line_offset + 1);
|
||||||
@ -2083,42 +2159,40 @@ pub const Module = struct {
|
|||||||
.external => false,
|
.external => false,
|
||||||
};
|
};
|
||||||
const flags = llvm.DI.Flags{};
|
const flags = llvm.DI.Flags{};
|
||||||
const is_definition = switch (global.variable.value.bb) {
|
const is_definition = switch (global.variable.storage.?.bb) {
|
||||||
.function => true,
|
.function => true,
|
||||||
else => @trap(),
|
else => @trap(),
|
||||||
};
|
};
|
||||||
const name = global.variable.name;
|
const name = global.variable.name;
|
||||||
const linkage_name = name;
|
const linkage_name = name;
|
||||||
const subprogram = module.llvm.di_builder.create_function(module.scope.llvm.?, name, linkage_name, module.llvm.file, global.variable.line, global_type.llvm.debug.?.to_subroutine(), local_to_unit, is_definition, scope_line, flags, module.build_mode.is_optimized());
|
const subprogram = module.llvm.di_builder.create_function(module.scope.llvm.?, name, linkage_name, module.llvm.file, global.variable.line, subroutine_type, local_to_unit, is_definition, scope_line, flags, module.build_mode.is_optimized());
|
||||||
function_value.set_subprogram(subprogram);
|
llvm_function_value.set_subprogram(subprogram);
|
||||||
|
|
||||||
break :blk @ptrCast(subprogram);
|
break :blk @ptrCast(subprogram);
|
||||||
} else undefined;
|
} else undefined;
|
||||||
global.variable.value.bb.function.scope.llvm = function_scope;
|
global.variable.storage.?.bb.function.scope.llvm = function_scope;
|
||||||
|
|
||||||
switch (global.variable.value.bb) {
|
const entry_block = module.llvm.context.create_basic_block("entry", llvm_function_value);
|
||||||
.function => {
|
global.variable.storage.?.bb.function.return_block = module.llvm.context.create_basic_block("ret_block", null);
|
||||||
const entry_block = module.llvm.context.create_basic_block("entry", function_value);
|
|
||||||
global.variable.value.bb.function.return_block = module.llvm.context.create_basic_block("ret_block", null);
|
|
||||||
|
|
||||||
module.llvm.builder.position_at_end(entry_block);
|
module.llvm.builder.position_at_end(entry_block);
|
||||||
module.llvm.builder.set_current_debug_location(null);
|
module.llvm.builder.set_current_debug_location(null);
|
||||||
|
|
||||||
var llvm_abi_argument_buffer: [64]*llvm.Argument = undefined;
|
var llvm_abi_argument_buffer: [64]*llvm.Argument = undefined;
|
||||||
function_value.get_arguments(&llvm_abi_argument_buffer);
|
llvm_function_value.get_arguments(&llvm_abi_argument_buffer);
|
||||||
|
|
||||||
const llvm_abi_arguments = llvm_abi_argument_buffer[0..function.abi_argument_types.len];
|
const llvm_abi_arguments = llvm_abi_argument_buffer[0..function_type.abi_argument_types.len];
|
||||||
|
|
||||||
const return_abi_kind = function.return_abi.flags.kind;
|
const return_abi_kind = function_type.return_abi.flags.kind;
|
||||||
switch (return_abi_kind) {
|
switch (return_abi_kind) {
|
||||||
.ignore => {},
|
.ignore => {},
|
||||||
.indirect => {
|
.indirect => {
|
||||||
const indirect_argument_index = @intFromBool(function.return_abi.flags.sret_after_this);
|
const indirect_argument_index = @intFromBool(function_type.return_abi.flags.sret_after_this);
|
||||||
if (function.return_abi.flags.sret_after_this) {
|
if (function_type.return_abi.flags.sret_after_this) {
|
||||||
@trap();
|
@trap();
|
||||||
}
|
}
|
||||||
global.variable.value.bb.function.return_alloca = llvm_abi_arguments[indirect_argument_index].to_value();
|
global.variable.storage.?.bb.function.return_alloca = llvm_abi_arguments[indirect_argument_index].to_value();
|
||||||
if (!function.return_abi.flags.indirect_by_value) {
|
if (!function_type.return_abi.flags.indirect_by_value) {
|
||||||
@trap();
|
@trap();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2126,15 +2200,15 @@ pub const Module = struct {
|
|||||||
@trap();
|
@trap();
|
||||||
},
|
},
|
||||||
else => {
|
else => {
|
||||||
const alloca = module.create_alloca(.{ .type = function.return_abi.semantic_type, .name = "retval" });
|
const alloca = module.create_alloca(.{ .type = function_type.return_abi.semantic_type, .name = "retval" });
|
||||||
global.variable.value.bb.function.return_alloca = alloca;
|
global.variable.storage.?.bb.function.return_alloca = alloca;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// const argument_variables = global.value.bb.function.arguments.add_many(semantic_argument_count);
|
// const argument_variables = global.value.bb.function.arguments.add_many(semantic_argument_count);
|
||||||
for (
|
for (
|
||||||
//semantic_arguments,
|
//semantic_arguments,
|
||||||
function.argument_abis, argument_variables, 0..) |
|
function_type.argument_abis, argument_variables, 0..) |
|
||||||
//semantic_argument,
|
//semantic_argument,
|
||||||
argument_abi, *argument_variable, argument_index| {
|
argument_abi, *argument_variable, argument_index| {
|
||||||
const abi_arguments = llvm_abi_arguments[argument_abi.abi_start..][0..argument_abi.abi_count];
|
const abi_arguments = llvm_abi_arguments[argument_abi.abi_start..][0..argument_abi.abi_count];
|
||||||
@ -2231,7 +2305,7 @@ pub const Module = struct {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(argument_abi.abi_count == 1);
|
assert(argument_abi.abi_count == 1);
|
||||||
const abi_argument_type = function.abi_argument_types[argument_abi.abi_start];
|
const abi_argument_type = function_type.abi_argument_types[argument_abi.abi_start];
|
||||||
const destination_size = pointer_type.get_byte_size() - argument_abi.attributes.direct.offset;
|
const destination_size = pointer_type.get_byte_size() - argument_abi.attributes.direct.offset;
|
||||||
const is_volatile = false;
|
const is_volatile = false;
|
||||||
_ = abi_argument_type;
|
_ = abi_argument_type;
|
||||||
@ -2289,7 +2363,7 @@ pub const Module = struct {
|
|||||||
// };
|
// };
|
||||||
|
|
||||||
// no pointer
|
// no pointer
|
||||||
const argument_type = argument_variable.variable.value.type.?.bb.pointer.type;
|
const argument_type = argument_variable.variable.storage.?.type.?.bb.pointer.type;
|
||||||
if (module.has_debug_info) {
|
if (module.has_debug_info) {
|
||||||
const always_preserve = true;
|
const always_preserve = true;
|
||||||
const flags = llvm.DI.Flags{};
|
const flags = llvm.DI.Flags{};
|
||||||
@ -2300,10 +2374,10 @@ pub const Module = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.analyze_block(global, global.variable.value.bb.function.main_block);
|
module.analyze_block(global, global.variable.storage.?.bb.function.main_block);
|
||||||
|
|
||||||
// Handle jump to the return block
|
// Handle jump to the return block
|
||||||
const return_block = global.variable.value.bb.function.return_block orelse module.report_error();
|
const return_block = global.variable.storage.?.bb.function.return_block orelse module.report_error();
|
||||||
|
|
||||||
if (module.llvm.builder.get_insert_block()) |current_basic_block| {
|
if (module.llvm.builder.get_insert_block()) |current_basic_block| {
|
||||||
assert(current_basic_block.get_terminator() == null);
|
assert(current_basic_block.get_terminator() == null);
|
||||||
@ -2335,23 +2409,23 @@ pub const Module = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// End function debug info
|
// End function debug info
|
||||||
if (function_value.get_subprogram()) |subprogram| {
|
if (llvm_function_value.get_subprogram()) |subprogram| {
|
||||||
module.llvm.di_builder.finalize_subprogram(subprogram);
|
module.llvm.di_builder.finalize_subprogram(subprogram);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (function.return_abi.semantic_type == module.noreturn_type or global.variable.value.bb.function.attributes.naked) {
|
if (function_type.return_abi.semantic_type == module.noreturn_type or global.variable.storage.?.bb.function.attributes.naked) {
|
||||||
@trap();
|
@trap();
|
||||||
} else if (function.return_abi.semantic_type == module.void_type) {
|
} else if (function_type.return_abi.semantic_type == module.void_type) {
|
||||||
module.llvm.builder.create_ret_void();
|
module.llvm.builder.create_ret_void();
|
||||||
} else {
|
} else {
|
||||||
const abi_kind = function.return_abi.flags.kind;
|
const abi_kind = function_type.return_abi.flags.kind;
|
||||||
const return_value: ?*llvm.Value = switch (abi_kind) {
|
const return_value: ?*llvm.Value = switch (abi_kind) {
|
||||||
.direct, .extend => blk: {
|
.direct, .extend => blk: {
|
||||||
const coerce_to_type = function.return_abi.get_coerce_to_type();
|
const coerce_to_type = function_type.return_abi.get_coerce_to_type();
|
||||||
const return_alloca = global.variable.value.bb.function.return_alloca orelse unreachable;
|
const return_alloca = global.variable.storage.?.bb.function.return_alloca orelse unreachable;
|
||||||
|
|
||||||
if (function.return_abi.semantic_type.is_abi_equal(coerce_to_type) and function.return_abi.attributes.direct.offset == 0) {
|
if (function_type.return_abi.semantic_type.is_abi_equal(coerce_to_type) and function_type.return_abi.attributes.direct.offset == 0) {
|
||||||
if (module.llvm.builder.find_return_value_dominating_store(return_alloca, function.return_abi.semantic_type.llvm.handle.?)) |store| {
|
if (module.llvm.builder.find_return_value_dominating_store(return_alloca, function_type.return_abi.semantic_type.llvm.handle.?)) |store| {
|
||||||
const store_instruction = store.to_instruction();
|
const store_instruction = store.to_instruction();
|
||||||
const return_value = store_instruction.to_value().get_operand(0);
|
const return_value = store_instruction.to_value().get_operand(0);
|
||||||
const alloca = store_instruction.to_value().get_operand(1);
|
const alloca = store_instruction.to_value().get_operand(1);
|
||||||
@ -2361,16 +2435,16 @@ pub const Module = struct {
|
|||||||
alloca.to_instruction().erase_from_parent();
|
alloca.to_instruction().erase_from_parent();
|
||||||
break :blk return_value;
|
break :blk return_value;
|
||||||
} else {
|
} else {
|
||||||
const load_value = module.create_load(.{ .type = function.return_abi.semantic_type, .value = return_alloca });
|
const load_value = module.create_load(.{ .type = function_type.return_abi.semantic_type, .value = return_alloca });
|
||||||
break :blk load_value;
|
break :blk load_value;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const source = switch (function.return_abi.attributes.direct.offset == 0) {
|
const source = switch (function_type.return_abi.attributes.direct.offset == 0) {
|
||||||
true => return_alloca,
|
true => return_alloca,
|
||||||
false => @trap(),
|
false => @trap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const source_type = function.return_abi.semantic_type;
|
const source_type = function_type.return_abi.semantic_type;
|
||||||
const destination_type = coerce_to_type;
|
const destination_type = coerce_to_type;
|
||||||
_ = source;
|
_ = source;
|
||||||
_ = source_type;
|
_ = source_type;
|
||||||
@ -2380,7 +2454,7 @@ pub const Module = struct {
|
|||||||
// break :blk result;
|
// break :blk result;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.indirect => switch (function.return_abi.semantic_type.get_evaluation_kind()) {
|
.indirect => switch (function_type.return_abi.semantic_type.get_evaluation_kind()) {
|
||||||
.complex => @trap(),
|
.complex => @trap(),
|
||||||
.aggregate => null,
|
.aggregate => null,
|
||||||
.scalar => @trap(),
|
.scalar => @trap(),
|
||||||
@ -2396,11 +2470,11 @@ pub const Module = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (lib.optimization_mode == .Debug) {
|
if (lib.optimization_mode == .Debug) {
|
||||||
const verify_result = function_value.verify();
|
const verify_result = llvm_function_value.verify();
|
||||||
if (!verify_result.success) {
|
if (!verify_result.success) {
|
||||||
lib.print_string(module.llvm.module.to_string());
|
lib.print_string(module.llvm.module.to_string());
|
||||||
lib.print_string("============================\n");
|
lib.print_string("============================\n");
|
||||||
lib.print_string(function_value.to_string());
|
lib.print_string(llvm_function_value.to_string());
|
||||||
lib.print_string("============================\n");
|
lib.print_string("============================\n");
|
||||||
lib.print_string(verify_result.error_message orelse unreachable);
|
lib.print_string(verify_result.error_message orelse unreachable);
|
||||||
lib.print_string("\n============================\n");
|
lib.print_string("\n============================\n");
|
||||||
@ -2408,9 +2482,6 @@ pub const Module = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => @trap(),
|
else => @trap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2478,76 +2549,17 @@ pub const Module = struct {
|
|||||||
type: ?*Type = null,
|
type: ?*Type = null,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn analyze_value(module: *Module, function: *Global, value: *Value, analysis: ValueAnalysis) void {
|
pub fn analyze(module: *Module, function: *Global, value: *Value, analysis: ValueAnalysis) void {
|
||||||
assert(value.type == null);
|
module.analyze_value_type(function, value, analysis);
|
||||||
|
module.emit_value(function, value, analysis);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emit_value(module: *Module, function: *Global, value: *Value, analysis: ValueAnalysis) void {
|
||||||
|
_ = function;
|
||||||
|
_ = analysis;
|
||||||
|
const value_type = value.type orelse unreachable;
|
||||||
assert(value.llvm == null);
|
assert(value.llvm == null);
|
||||||
|
|
||||||
if (analysis.type) |expected_type| switch (expected_type.bb) {
|
|
||||||
.integer => |integer| switch (value.bb) {
|
|
||||||
.constant_integer => |constant_integer| switch (constant_integer.signed) {
|
|
||||||
true => {
|
|
||||||
if (!integer.signed) {
|
|
||||||
module.report_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
@trap();
|
|
||||||
},
|
|
||||||
false => {
|
|
||||||
const type_max = (@as(u64, 1) << @intCast(integer.bit_count)) - 1;
|
|
||||||
if (constant_integer.value > type_max) {
|
|
||||||
module.report_error();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.unary => |unary| {
|
|
||||||
switch (unary.id) {
|
|
||||||
.@"+" => @trap(),
|
|
||||||
.@"-" => {
|
|
||||||
module.analyze_value(function, unary.value, analysis);
|
|
||||||
if (!unary.value.type.?.is_signed()) {
|
|
||||||
module.report_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(expected_type == unary.value.type);
|
|
||||||
},
|
|
||||||
.@"&" => @trap(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.binary => |binary| {
|
|
||||||
const is_boolean = switch (binary.id) {
|
|
||||||
.@"==",
|
|
||||||
.@"!=",
|
|
||||||
.@">",
|
|
||||||
.@"<",
|
|
||||||
.@">=",
|
|
||||||
.@"<=",
|
|
||||||
=> true,
|
|
||||||
else => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
const boolean_type = module.integer_type(1, false);
|
|
||||||
|
|
||||||
if (is_boolean and expected_type != boolean_type) {
|
|
||||||
module.report_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
module.analyze_value(function, binary.left, .{
|
|
||||||
.type = if (is_boolean) null else expected_type,
|
|
||||||
});
|
|
||||||
|
|
||||||
module.analyze_value(function, binary.right, .{
|
|
||||||
.type = binary.left.type,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
else => @trap(),
|
|
||||||
},
|
|
||||||
else => @trap(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const value_type = if (analysis.type) |expected_type| expected_type else switch (value.bb) {
|
|
||||||
else => @trap(),
|
|
||||||
};
|
|
||||||
|
|
||||||
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.llvm.handle.?.to_integer().get_constant(constant_integer.value, @intFromBool(constant_integer.signed)).to_value(),
|
||||||
.unary => |unary| switch (unary.id) {
|
.unary => |unary| switch (unary.id) {
|
||||||
@ -2596,11 +2608,97 @@ pub const Module = struct {
|
|||||||
},
|
},
|
||||||
else => @trap(),
|
else => @trap(),
|
||||||
},
|
},
|
||||||
|
.variable_reference => |variable| if (variable.type == value_type) switch (value_type.get_evaluation_kind()) {
|
||||||
|
.scalar => module.create_load(.{
|
||||||
|
.type = value_type,
|
||||||
|
.value = variable.storage.?.llvm.?,
|
||||||
|
.alignment = variable.storage.?.type.?.bb.pointer.alignment,
|
||||||
|
}),
|
||||||
|
.aggregate => @trap(),
|
||||||
|
.complex => @trap(),
|
||||||
|
} else @trap(),
|
||||||
|
else => @trap(),
|
||||||
|
};
|
||||||
|
|
||||||
|
value.llvm = llvm_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn analyze_value_type(module: *Module, function: *Global, value: *Value, analysis: ValueAnalysis) void {
|
||||||
|
assert(value.type == null);
|
||||||
|
assert(value.llvm == null);
|
||||||
|
|
||||||
|
if (analysis.type) |expected_type| switch (expected_type.bb) {
|
||||||
|
.integer => |integer| switch (value.bb) {
|
||||||
|
.constant_integer => |constant_integer| switch (constant_integer.signed) {
|
||||||
|
true => {
|
||||||
|
if (!integer.signed) {
|
||||||
|
module.report_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
@trap();
|
||||||
|
},
|
||||||
|
false => {
|
||||||
|
const type_max = (@as(u64, 1) << @intCast(integer.bit_count)) - 1;
|
||||||
|
if (constant_integer.value > type_max) {
|
||||||
|
module.report_error();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.unary => |unary| {
|
||||||
|
switch (unary.id) {
|
||||||
|
.@"+" => @trap(),
|
||||||
|
.@"-" => {
|
||||||
|
module.analyze(function, unary.value, analysis);
|
||||||
|
if (!unary.value.type.?.is_signed()) {
|
||||||
|
module.report_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(expected_type == unary.value.type);
|
||||||
|
},
|
||||||
|
.@"&" => @trap(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.binary => |binary| {
|
||||||
|
const is_boolean = switch (binary.id) {
|
||||||
|
.@"==",
|
||||||
|
.@"!=",
|
||||||
|
.@">",
|
||||||
|
.@"<",
|
||||||
|
.@">=",
|
||||||
|
.@"<=",
|
||||||
|
=> true,
|
||||||
|
else => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const boolean_type = module.integer_type(1, false);
|
||||||
|
|
||||||
|
if (is_boolean and expected_type != boolean_type) {
|
||||||
|
module.report_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
module.analyze(function, binary.left, .{
|
||||||
|
.type = if (is_boolean) null else expected_type,
|
||||||
|
});
|
||||||
|
|
||||||
|
module.analyze(function, binary.right, .{
|
||||||
|
.type = binary.left.type,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
.variable_reference => |variable| {
|
||||||
|
if (variable.type != expected_type) {
|
||||||
|
module.report_error();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => @trap(),
|
||||||
|
},
|
||||||
|
else => @trap(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const value_type = if (analysis.type) |expected_type| expected_type else switch (value.bb) {
|
||||||
else => @trap(),
|
else => @trap(),
|
||||||
};
|
};
|
||||||
|
|
||||||
value.type = value_type;
|
value.type = value_type;
|
||||||
value.llvm = llvm_value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn analyze_block(module: *Module, function: *Global, block: *LexicalBlock) void {
|
pub fn analyze_block(module: *Module, function: *Global, block: *LexicalBlock) void {
|
||||||
@ -2626,7 +2724,7 @@ pub const Module = struct {
|
|||||||
|
|
||||||
switch (statement.bb) {
|
switch (statement.bb) {
|
||||||
.@"return" => |rv| {
|
.@"return" => |rv| {
|
||||||
const function_type = &function.variable.value.type.?.bb.pointer.type.bb.function;
|
const function_type = &function.variable.storage.?.type.?.bb.pointer.type.bb.function;
|
||||||
const return_abi = function_type.return_abi;
|
const return_abi = function_type.return_abi;
|
||||||
|
|
||||||
switch (return_abi.semantic_type.bb) {
|
switch (return_abi.semantic_type.bb) {
|
||||||
@ -2638,7 +2736,7 @@ pub const Module = struct {
|
|||||||
.noreturn => module.report_error(),
|
.noreturn => module.report_error(),
|
||||||
else => {
|
else => {
|
||||||
const return_value = rv orelse module.report_error();
|
const return_value = rv orelse module.report_error();
|
||||||
module.analyze_value(function, return_value, .{
|
module.analyze(function, return_value, .{
|
||||||
.type = return_abi.semantic_type,
|
.type = return_abi.semantic_type,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -2647,7 +2745,7 @@ pub const Module = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clang equivalent: CodeGenFunction::EmitReturnStmt
|
// Clang equivalent: CodeGenFunction::EmitReturnStmt
|
||||||
const return_alloca = function.variable.value.bb.function.return_alloca orelse module.report_error();
|
const return_alloca = function.variable.storage.?.bb.function.return_alloca orelse module.report_error();
|
||||||
|
|
||||||
switch (return_abi.semantic_type.get_evaluation_kind()) {
|
switch (return_abi.semantic_type.get_evaluation_kind()) {
|
||||||
.scalar => {
|
.scalar => {
|
||||||
@ -2728,15 +2826,83 @@ pub const Module = struct {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const return_block = function.variable.value.bb.function.return_block orelse module.report_error();
|
const return_block = function.variable.storage.?.bb.function.return_block orelse module.report_error();
|
||||||
|
|
||||||
_ = module.llvm.builder.create_branch(return_block);
|
_ = module.llvm.builder.create_branch(return_block);
|
||||||
_ = module.llvm.builder.clear_insertion_position();
|
_ = module.llvm.builder.clear_insertion_position();
|
||||||
},
|
},
|
||||||
|
.local => |local| {
|
||||||
|
if (local.variable.type) |expected_type| {
|
||||||
|
assert(local.variable.storage == null);
|
||||||
|
module.analyze_value_type(function, local.variable.initial_value, .{ .type = local.variable.type });
|
||||||
|
local.variable.resolve_type(local.variable.initial_value.type.?);
|
||||||
|
assert(expected_type == local.variable.type);
|
||||||
|
module.emit_local_storage(local, last_statement_debug_location);
|
||||||
|
|
||||||
|
|
||||||
|
module.emit_assignment(function, local.variable.storage.?, local.variable.initial_value);
|
||||||
|
} else {
|
||||||
|
@trap();
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn emit_assignment(module: *Module, function: *Global, left: *Value, right: *Value) void {
|
||||||
|
assert(left.llvm != null);
|
||||||
|
assert(right.llvm == null);
|
||||||
|
const pointer_type = left.type.?;
|
||||||
|
const value_type = right.type.?;
|
||||||
|
assert(pointer_type.bb == .pointer);
|
||||||
|
assert(pointer_type.bb.pointer.type == value_type);
|
||||||
|
|
||||||
|
switch (value_type.get_evaluation_kind()) {
|
||||||
|
.scalar => {
|
||||||
|
module.emit_value(function, right, .{});
|
||||||
|
_ = module.create_store(.{
|
||||||
|
.source_value = right.llvm.?,
|
||||||
|
.destination_value = left.llvm.?,
|
||||||
|
.source_type = value_type,
|
||||||
|
.destination_type = value_type,
|
||||||
|
.alignment = pointer_type.bb.pointer.alignment,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
.aggregate => @trap(),
|
||||||
|
.complex => @trap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emit_local_storage(module: *Module, local: *Local, statement_debug_location: *llvm.DI.Location) void {
|
||||||
|
assert(local.variable.storage == null);
|
||||||
|
const resolved_type = local.variable.type.?;
|
||||||
|
const pointer_type = module.get_pointer_type(.{ .type = resolved_type });
|
||||||
|
const storage = module.values.add();
|
||||||
|
storage.* = .{
|
||||||
|
.type = pointer_type,
|
||||||
|
.bb = .local,
|
||||||
|
.llvm = module.create_alloca(.{
|
||||||
|
.type = pointer_type.bb.pointer.type,
|
||||||
|
.alignment = pointer_type.bb.pointer.alignment,
|
||||||
|
.name = local.variable.name,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
if (module.has_debug_info) {
|
||||||
|
module.llvm.builder.set_current_debug_location(statement_debug_location);
|
||||||
|
const debug_type = resolved_type.llvm.debug.?;
|
||||||
|
const always_preserve = true;
|
||||||
|
// TODO:
|
||||||
|
const alignment = 0;
|
||||||
|
const flags = llvm.DI.Flags{};
|
||||||
|
const local_variable = module.llvm.di_builder.create_auto_variable(local.variable.scope.llvm.?, local.variable.name, module.llvm.file, local.variable.line, debug_type, always_preserve, flags, alignment);
|
||||||
|
const inlined_at: ?*llvm.DI.Metadata = null; // TODO
|
||||||
|
const debug_location = llvm.DI.create_debug_location(module.llvm.context, local.variable.line, local.variable.column, local.variable.scope.llvm.?, inlined_at);
|
||||||
|
_ = module.llvm.di_builder.insert_declare_record_at_end(storage.llvm.?, local_variable, module.llvm.di_builder.null_expression(), debug_location, module.llvm.builder.get_insert_block().?);
|
||||||
|
module.llvm.builder.set_current_debug_location(statement_debug_location);
|
||||||
|
}
|
||||||
|
local.variable.storage = storage;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn align_integer_type(module: *Module, ty: *Type) *Type {
|
pub fn align_integer_type(module: *Module, ty: *Type) *Type {
|
||||||
assert(ty.bb == .integer);
|
assert(ty.bb == .integer);
|
||||||
const bit_count = ty.get_bit_size();
|
const bit_count = ty.get_bit_size();
|
||||||
@ -3923,6 +4089,7 @@ pub fn compile(arena: *Arena, options: Options) void {
|
|||||||
.line_character_offset = 0,
|
.line_character_offset = 0,
|
||||||
.types = types,
|
.types = types,
|
||||||
.globals = globals,
|
.globals = globals,
|
||||||
|
.locals = .initialize(),
|
||||||
.values = values,
|
.values = values,
|
||||||
.pointer_types = .initialize(),
|
.pointer_types = .initialize(),
|
||||||
.lexical_blocks = .initialize(),
|
.lexical_blocks = .initialize(),
|
||||||
|
@ -182,7 +182,7 @@ const names = &[_][]const u8{
|
|||||||
"constant_xor",
|
"constant_xor",
|
||||||
"constant_shift_left",
|
"constant_shift_left",
|
||||||
"constant_shift_right",
|
"constant_shift_right",
|
||||||
// "minimal_stack",
|
"minimal_stack",
|
||||||
// "minimal_stack_arithmetic",
|
// "minimal_stack_arithmetic",
|
||||||
// "pointer",
|
// "pointer",
|
||||||
// "extend",
|
// "extend",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user