implement page allocator

This commit is contained in:
David Gonzalez Martin 2023-11-24 19:20:59 -06:00
parent 48736fd7c3
commit 5145110fbf
14 changed files with 3626 additions and 1031 deletions

View File

@ -17,6 +17,7 @@ const StringHashMap = data_structures.StringHashMap;
const StringArrayHashMap = data_structures.StringArrayHashMap; const StringArrayHashMap = data_structures.StringArrayHashMap;
const lexical_analyzer = @import("frontend/lexical_analyzer.zig"); const lexical_analyzer = @import("frontend/lexical_analyzer.zig");
const Token = lexical_analyzer.Token;
const syntactic_analyzer = @import("frontend/syntactic_analyzer.zig"); const syntactic_analyzer = @import("frontend/syntactic_analyzer.zig");
const Node = syntactic_analyzer.Node; const Node = syntactic_analyzer.Node;
const semantic_analyzer = @import("frontend/semantic_analyzer.zig"); const semantic_analyzer = @import("frontend/semantic_analyzer.zig");
@ -177,7 +178,28 @@ pub fn init(allocator: Allocator) !void {
pub const Struct = struct { pub const Struct = struct {
scope: Scope.Index, scope: Scope.Index,
fields: ArrayList(Field.Index) = .{}, fields: ArrayList(ContainerField.Index) = .{},
backing_type: Type.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
};
pub const ContainerField = struct {
name: u32,
type: Type.Index,
default_value: Value.Index,
parent: Type.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
};
pub const ContainerInitialization = struct {
field_initializations: ArrayList(Value.Index),
type: Type.Index,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
@ -214,27 +236,36 @@ pub const Array = struct {
}; };
pub const Type = union(enum) { pub const Type = union(enum) {
any,
void, void,
noreturn, noreturn,
bool, bool,
type, type,
comptime_int, comptime_int,
integer: Type.Integer, integer: Type.Integer,
slice: Slice, slice: Type.Slice,
pointer: Pointer, pointer: Pointer,
@"struct": Struct.Index, @"struct": Struct.Index,
@"enum": Enum.Index, @"enum": Enum.Index,
function: Function.Prototype.Index, function: Function.Prototype.Index,
array: Array, array: Array,
optional: Optional,
const Slice = struct { const Optional = struct {
element_type: Type.Index, element_type: Type.Index,
}; };
const Pointer = struct {
pub const Slice = struct {
element_type: Type.Index,
@"const": bool,
};
pub const Pointer = struct {
element_type: Type.Index, element_type: Type.Index,
many: bool, many: bool,
@"const": bool, @"const": bool,
}; };
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
pub const Allocation = List.Allocation; pub const Allocation = List.Allocation;
@ -282,6 +313,16 @@ pub const Type = union(enum) {
}; };
} }
pub fn getBitSize(type_info: Type) u64 {
return switch (type_info) {
.integer => |integer| integer.bit_count,
.pointer => 8,
.bool => 1,
.comptime_int => @panic("This call should never happen"),
else => |t| @panic(@tagName(t)),
};
}
pub fn getAlignment(type_info: Type) u64 { pub fn getAlignment(type_info: Type) u64 {
return switch (type_info) { return switch (type_info) {
.integer => |integer| @min(16, integer.getSize()), .integer => |integer| @min(16, integer.getSize()),
@ -290,6 +331,7 @@ pub const Type = union(enum) {
}; };
} }
pub const any = FixedTypeKeyword.any.toType();
pub const @"void" = FixedTypeKeyword.void.toType(); pub const @"void" = FixedTypeKeyword.void.toType();
pub const boolean = FixedTypeKeyword.bool.toType(); pub const boolean = FixedTypeKeyword.bool.toType();
pub const ssize = FixedTypeKeyword.ssize.toType(); pub const ssize = FixedTypeKeyword.ssize.toType();
@ -318,6 +360,7 @@ pub const Type = union(enum) {
// Each time an enum is added here, a corresponding insertion in the initialization must be made // Each time an enum is added here, a corresponding insertion in the initialization must be made
pub const Intrinsic = enum { pub const Intrinsic = enum {
//@"asm", this is processed separately as it need special parsing
@"error", @"error",
import, import,
syscall, syscall,
@ -332,6 +375,7 @@ pub const FixedTypeKeyword = enum {
ssize, ssize,
type, type,
comptime_int, comptime_int,
any,
const offset = 0; const offset = 0;
@ -382,9 +426,10 @@ pub const extra_common_type_data = blk: {
/// A scope contains a bunch of declarations /// A scope contains a bunch of declarations
pub const Scope = struct { pub const Scope = struct {
declarations: AutoHashMap(u32, Declaration.Index) = .{}, declarations: data_structures.AutoArrayHashMap(u32, Declaration.Index) = .{},
parent: Scope.Index, parent: Scope.Index,
file: File.Index, file: File.Index,
token: Token.Index,
type: Type.Index = Type.Index.invalid, type: Type.Index = Type.Index.invalid,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
@ -409,6 +454,7 @@ pub const Declaration = struct {
name: u32, name: u32,
argument_index: ?u32, argument_index: ?u32,
type: Type.Index, type: Type.Index,
scope: Scope.Index,
pub const Reference = struct { pub const Reference = struct {
value: Declaration.Index, value: Declaration.Index,
@ -465,15 +511,6 @@ pub const Block = struct {
pub const Allocation = List.Allocation; pub const Allocation = List.Allocation;
}; };
pub const Field = struct {
name: u32,
type: Type.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
};
pub const Loop = struct { pub const Loop = struct {
condition: Value.Index, condition: Value.Index,
body: Value.Index, body: Value.Index,
@ -484,10 +521,6 @@ pub const Loop = struct {
pub const Allocation = List.Allocation; pub const Allocation = List.Allocation;
}; };
const Runtime = struct {
foo: u32 = 0,
};
const Unresolved = struct { const Unresolved = struct {
node_index: Node.Index, node_index: Node.Index,
}; };
@ -519,6 +552,7 @@ pub const Call = struct {
value: Value.Index, value: Value.Index,
arguments: ArgumentList.Index, arguments: ArgumentList.Index,
type: Type.Index, type: Type.Index,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
pub const Allocation = List.Allocation; pub const Allocation = List.Allocation;
@ -560,25 +594,47 @@ pub const BinaryOperation = struct {
pub const Id = enum { pub const Id = enum {
add, add,
sub, sub,
logical_and, bit_and,
logical_xor, bit_xor,
logical_or, bit_or,
multiply, multiply,
divide, divide,
shift_left, shift_left,
shift_right, shift_right,
compare_equal, compare_equal,
compare_greater_than,
compare_greater_or_equal,
compare_less_than,
compare_less_or_equal,
};
};
pub const UnaryOperation = struct {
value: Value.Index,
type: Type.Index,
id: Id,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
pub const Id = enum {
boolean_not,
negation,
address_of,
pointer_dereference,
}; };
}; };
pub const CallingConvention = enum { pub const CallingConvention = enum {
system_v, system_v,
naked,
}; };
pub const Branch = struct { pub const Branch = struct {
condition: Value.Index, expression: Value.Index,
true_expression: Value.Index, taken_expression: Value.Index,
false_expression: Value.Index, not_taken_expression: Value.Index,
reaches_end: bool, reaches_end: bool,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
@ -586,19 +642,118 @@ pub const Branch = struct {
pub const Allocation = List.Allocation; pub const Allocation = List.Allocation;
}; };
pub const FieldAccess = struct {
declaration_reference: Value.Index,
field: ContainerField.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
};
pub const Slice = struct {
sliceable: Value.Index,
start: Value.Index,
end: Value.Index,
type: Type.Index,
pub const Access = struct {
value: Value.Index,
field: Field,
type: Type.Index,
pub const List = BlockList(@This());
pub const Index = Slice.Access.List.Index;
pub const Allocation = Slice.Access.List.Allocation;
};
pub const Field = enum {
ptr,
len,
};
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
};
pub const IndexedAccess = struct {
indexed_expression: Value.Index,
index_expression: Value.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
};
pub const OptionalCheck = struct {
value: Value.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
};
pub const OptionalUnwrap = struct {
value: Value.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
};
pub const Assembly = struct {
pub const Instruction = struct {
id: u32,
operands: []const Operand,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
};
pub const Operand = union(enum) {
register: u32,
number_literal: u64,
value_index: Value.Index,
};
pub const Block = struct {
instructions: []const Assembly.Instruction.Index,
pub const List = BlockList(@This());
pub const Index = List.Index;
pub const Allocation = List.Allocation;
};
pub const x86_64 = struct {
pub const Instruction = enum {
@"and",
call,
xor,
};
pub const Register = enum {
ebp,
rsp,
};
};
};
pub const Value = union(enum) { pub const Value = union(enum) {
unresolved: Unresolved,
declaration: Declaration.Index,
declaration_reference: Declaration.Reference,
void, void,
bool: bool, bool: bool,
undefined, undefined,
@"unreachable", @"unreachable",
pointer_null_literal,
optional_null_literal,
unresolved: Unresolved,
declaration: Declaration.Index,
declaration_reference: Declaration.Reference,
loop: Loop.Index, loop: Loop.Index,
function_definition: Function.Index, function_definition: Function.Index,
function_declaration: Function.Index, function_declaration: Function.Index,
block: Block.Index, block: Block.Index,
runtime: Runtime,
assign: Assignment.Index, assign: Assignment.Index,
type: Type.Index, type: Type.Index,
integer: Integer, integer: Integer,
@ -613,8 +768,17 @@ pub const Value = union(enum) {
sign_extend: Cast.Index, sign_extend: Cast.Index,
zero_extend: Cast.Index, zero_extend: Cast.Index,
binary_operation: BinaryOperation.Index, binary_operation: BinaryOperation.Index,
unary_operation: UnaryOperation.Index,
branch: Branch.Index, branch: Branch.Index,
cast: Cast.Index, cast: Cast.Index,
container_initialization: ContainerInitialization.Index,
field_access: FieldAccess.Index,
slice_access: Slice.Access.Index,
indexed_access: IndexedAccess.Index,
optional_check: OptionalCheck.Index,
optional_unwrap: OptionalUnwrap.Index,
slice: Slice.Index,
assembly_block: Assembly.Block.Index,
pub const List = BlockList(@This()); pub const List = BlockList(@This());
pub const Index = List.Index; pub const Index = List.Index;
@ -632,11 +796,20 @@ pub const Value = union(enum) {
pub fn isComptime(value: *Value, module: *Module) bool { pub fn isComptime(value: *Value, module: *Module) bool {
return switch (value.*) { return switch (value.*) {
.bool, .void, .undefined, .function_definition, .type, .enum_field => true,
.integer => |integer| integer.type.eq(Type.comptime_int), .integer => |integer| integer.type.eq(Type.comptime_int),
.call => false,
.binary_operation => false,
.declaration_reference => |declaration_reference| module.declarations.get(declaration_reference.value).mutability == .@"const" and isComptime(module.values.get(module.declarations.get(declaration_reference.value).init_value), module), .declaration_reference => |declaration_reference| module.declarations.get(declaration_reference.value).mutability == .@"const" and isComptime(module.values.get(module.declarations.get(declaration_reference.value).init_value), module),
.bool, .void, .undefined, .function_definition, .type, .enum_field => true,
// TODO:
.call,
.syscall,
.binary_operation,
.container_initialization,
.cast,
.optional_unwrap,
.pointer_null_literal,
.indexed_access,
=> false,
// TODO:
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
} }
@ -654,73 +827,36 @@ pub const Value = union(enum) {
.binary_operation => |binary_operation| module.binary_operations.get(binary_operation).type, .binary_operation => |binary_operation| module.binary_operations.get(binary_operation).type,
.bool => Type.boolean, .bool => Type.boolean,
.declaration => Type.void, .declaration => Type.void,
.container_initialization => |container_initialization| module.container_initializations.get(container_initialization).type,
.syscall => Type.usize,
.unary_operation => |unary_operation_index| module.unary_operations.get(unary_operation_index).type,
.pointer_null_literal => semantic_analyzer.optional_pointer_to_any_type,
.optional_null_literal => semantic_analyzer.optional_any,
.field_access => |field_access_index| module.container_fields.get(module.field_accesses.get(field_access_index).field).type,
.cast => |cast_index| module.casts.get(cast_index).type,
.slice => |slice_index| module.slices.get(slice_index).type,
.slice_access => |slice_access_index| module.slice_accesses.get(slice_access_index).type,
.optional_check => Type.boolean,
.indexed_access => |indexed_access_index| blk: {
const indexed_expression = module.values.get(module.indexed_accesses.get(indexed_access_index).indexed_expression);
const indexed_expression_type_index = indexed_expression.getType(module);
const indexed_expression_type = module.types.get(indexed_expression_type_index);
break :blk switch (indexed_expression_type.*) {
.slice => |slice| slice.element_type,
else => |t| @panic(@tagName(t)),
};
},
else => |t| @panic(@tagName(t)), else => |t| @panic(@tagName(t)),
}; };
return result; return result;
} }
// pub fn setType(value: *Value, new_type: Type.Index) void {
// switch (value.*) {
// .integer => value.integer.type = new_type,
// else => |t| @panic(@tagName(t)),
// }
// }
const TypeCheckError = error{ const TypeCheckError = error{
integer_size, integer_size,
pointer_many_differ, pointer_many_differ,
pointer_element_type_differ, pointer_element_type_differ,
}; };
pub fn typeCheck(value: *Value, module: *Module, type_to_check_index: Type.Index) TypeCheckError!void {
const value_type_index = value.getType(module);
if (!value_type_index.eq(type_to_check_index)) {
const value_type = module.types.get(value_type_index);
const check_type = module.types.get(type_to_check_index);
if (std.meta.activeTag(value_type.*) == std.meta.activeTag(check_type.*)) {
switch (value_type.*) {
.integer => |coercee_int| {
if (check_type.integer.getSize() < coercee_int.getSize()) {
return error.integer_size;
}
},
.pointer => |coercee_pointer| {
if (coercee_pointer.many != check_type.pointer.many) {
return error.pointer_many_differ;
}
if (!coercee_pointer.element_type.eq(check_type.pointer.element_type)) {
if (check_type.pointer.many) {
const coercee_element_type = module.types.get(coercee_pointer.element_type);
switch (coercee_element_type.*) {
.array => |array| if (!array.element_type.eq(check_type.pointer.element_type)) {
return error.pointer_element_type_differ;
},
else => |t| @panic(@tagName(t)),
}
}
}
},
else => |t| @panic(@tagName(t)),
}
} else {
switch (check_type.*) {
.integer => {
switch (value_type.*) {
.comptime_int => switch (value.*) {
.integer => value.integer.type = type_to_check_index,
.declaration_reference => value.declaration_reference.type = type_to_check_index,
else => |t| @panic(@tagName(t)),
},
else => |t| @panic(@tagName(t)),
}
},
else => |t| @panic(@tagName(t)),
}
}
}
}
}; };
pub const Module = struct { pub const Module = struct {
@ -735,7 +871,6 @@ pub const Module = struct {
function_definitions: BlockList(Function) = .{}, function_definitions: BlockList(Function) = .{},
function_declarations: BlockList(Function) = .{}, function_declarations: BlockList(Function) = .{},
function_prototypes: BlockList(Function.Prototype) = .{}, function_prototypes: BlockList(Function.Prototype) = .{},
fields: BlockList(Field) = .{},
types: BlockList(Type) = .{}, types: BlockList(Type) = .{},
blocks: BlockList(Block) = .{}, blocks: BlockList(Block) = .{},
loops: BlockList(Loop) = .{}, loops: BlockList(Loop) = .{},
@ -747,12 +882,28 @@ pub const Module = struct {
string_literals: StringKeyMap([]const u8) = .{}, string_literals: StringKeyMap([]const u8) = .{},
enums: BlockList(Enum) = .{}, enums: BlockList(Enum) = .{},
enum_fields: BlockList(Enum.Field) = .{}, enum_fields: BlockList(Enum.Field) = .{},
function_name_map: data_structures.AutoArrayHashMap(Function.Index, u32) = .{}, container_fields: BlockList(ContainerField) = .{},
container_initializations: BlockList(ContainerInitialization) = .{},
function_map: data_structures.AutoArrayHashMap(Function.Index, Declaration.Index) = .{},
type_map: data_structures.AutoArrayHashMap(Type.Index, Declaration.Index) = .{},
arrays: BlockList(Array) = .{}, arrays: BlockList(Array) = .{},
casts: BlockList(Cast) = .{}, casts: BlockList(Cast) = .{},
binary_operations: BlockList(BinaryOperation) = .{}, binary_operations: BlockList(BinaryOperation) = .{},
unary_operations: BlockList(UnaryOperation) = .{},
branches: BlockList(Branch) = .{}, branches: BlockList(Branch) = .{},
field_accesses: BlockList(FieldAccess) = .{},
slices: BlockList(Slice) = .{},
slice_accesses: BlockList(Slice.Access) = .{},
indexed_accesses: BlockList(IndexedAccess) = .{},
optional_checks: BlockList(OptionalCheck) = .{},
optional_unwraps: BlockList(OptionalUnwrap) = .{},
assembly_blocks: BlockList(Assembly.Block) = .{},
assembly_instructions: BlockList(Assembly.Instruction) = .{},
non_primitive_integer_types: data_structures.AutoArrayHashMap(Type.Integer, Type.Index) = .{},
string_literal_types: data_structures.AutoArrayHashMap(u32, Type.Index) = .{}, string_literal_types: data_structures.AutoArrayHashMap(u32, Type.Index) = .{},
slice_types: data_structures.AutoArrayHashMap(Type.Slice, Type.Index) = .{},
pointer_types: data_structures.AutoArrayHashMap(Type.Pointer, Type.Index) = .{},
optional_types: data_structures.AutoArrayHashMap(Type.Index, Type.Index) = .{},
array_types: data_structures.AutoArrayHashMap(Array, Type.Index) = .{}, array_types: data_structures.AutoArrayHashMap(Array, Type.Index) = .{},
entry_point: Function.Index = Function.Index.invalid, entry_point: Function.Index = Function.Index.invalid,
descriptor: Descriptor, descriptor: Descriptor,
@ -1074,8 +1225,27 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) !
for (extra_common_type_data) |type_data| { for (extra_common_type_data) |type_data| {
_ = try module.types.append(compilation.base_allocator, type_data); _ = try module.types.append(compilation.base_allocator, type_data);
} }
semantic_analyzer.pointer_to_any_type = (try module.types.append(compilation.base_allocator, .{
.pointer = .{
.element_type = Type.any,
.many = false,
.@"const" = true,
},
})).index;
semantic_analyzer.optional_pointer_to_any_type = (try module.types.append(compilation.base_allocator, .{
.optional = .{
.element_type = semantic_analyzer.pointer_to_any_type,
},
})).index;
semantic_analyzer.optional_any = (try module.types.append(compilation.base_allocator, .{
.optional = .{
.element_type = Type.any,
},
})).index;
semantic_analyzer.unreachable_index = (try module.values.append(compilation.base_allocator, .@"unreachable")).index; semantic_analyzer.unreachable_index = (try module.values.append(compilation.base_allocator, .@"unreachable")).index;
semantic_analyzer.pointer_null_index = (try module.values.append(compilation.base_allocator, .pointer_null_literal)).index;
semantic_analyzer.optional_null_index = (try module.values.append(compilation.base_allocator, .optional_null_literal)).index;
const value_allocation = try module.values.append(compilation.base_allocator, .{ const value_allocation = try module.values.append(compilation.base_allocator, .{
.unresolved = .{ .unresolved = .{

File diff suppressed because it is too large Load Diff

View File

@ -46,6 +46,10 @@ pub const Token = packed struct(u64) {
fixed_keyword_enum = 0x17, fixed_keyword_enum = 0x17,
fixed_keyword_union = 0x18, fixed_keyword_union = 0x18,
fixed_keyword_extern = 0x19, fixed_keyword_extern = 0x19,
fixed_keyword_null = 0x1a,
fixed_keyword_align = 0x1b,
fixed_keyword_export = 0x1c,
fixed_keyword_cc = 0x1d,
keyword_unsigned_integer = 0x1f, keyword_unsigned_integer = 0x1f,
keyword_signed_integer = 0x20, keyword_signed_integer = 0x20,
bang = '!', // 0x21 bang = '!', // 0x21
@ -106,6 +110,10 @@ pub const FixedKeyword = enum {
@"enum", @"enum",
@"union", @"union",
@"extern", @"extern",
null,
@"align",
@"export",
cc,
}; };
pub const Result = struct { pub const Result = struct {
@ -169,10 +177,6 @@ pub fn analyze(allocator: Allocator, text: []const u8, file_index: File.Index) !
inline else => |comptime_fixed_keyword| @field(Token.Id, "fixed_keyword_" ++ @tagName(comptime_fixed_keyword)), inline else => |comptime_fixed_keyword| @field(Token.Id, "fixed_keyword_" ++ @tagName(comptime_fixed_keyword)),
} else .identifier; } else .identifier;
}, },
'(', ')', '{', '}', '[', ']', '=', ';', '#', '@', ',', '.', ':', '>', '<', '!', '+', '-', '*', '\\', '/', '&', '|', '^' => |operator| blk: {
index += 1;
break :blk @enumFromInt(operator);
},
'0'...'9' => blk: { '0'...'9' => blk: {
// Detect other non-decimal literals // Detect other non-decimal literals
if (text[index] == '0' and index + 1 < text.len) { if (text[index] == '0' and index + 1 < text.len) {
@ -217,6 +221,10 @@ pub fn analyze(allocator: Allocator, text: []const u8, file_index: File.Index) !
index += 1; index += 1;
continue; continue;
}, },
'(', ')', '{', '}', '[', ']', '=', ';', '#', '@', ',', '.', ':', '>', '<', '!', '+', '-', '*', '\\', '/', '&', '|', '^', '?', '$' => |operator| blk: {
index += 1;
break :blk @enumFromInt(operator);
},
else => |ch| { else => |ch| {
std.debug.panic("NI: '{c}'", .{ch}); std.debug.panic("NI: '{c}'", .{ch});
}, },

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,9 +5,10 @@ const Compilation = @import("Compilation.zig");
pub const panic = Compilation.panic; pub const panic = Compilation.panic;
pub fn main() !void { pub fn main() !void {
const allocator = std.heap.page_allocator; const GPA = std.heap.GeneralPurposeAllocator(.{});
var gpa = GPA{};
try Compilation.init(allocator); try Compilation.init(gpa.allocator());
} }
test { test {

View File

@ -29,6 +29,7 @@ pub fn build(b: *std.Build) !void {
const debug_command = switch (@import("builtin").os.tag) { const debug_command = switch (@import("builtin").os.tag) {
.linux => blk: { .linux => blk: {
const result = b.addSystemCommand(&.{"gf2"}); const result = b.addSystemCommand(&.{"gf2"});
result.addArgs(&.{ "-ex", "set disassembly-flavor intel" });
result.addArg("-ex=r"); result.addArg("-ex=r");
result.addArgs(&.{ "-ex", "up" }); result.addArgs(&.{ "-ex", "up" });
result.addArg("--args"); result.addArg("--args");

View File

@ -14,3 +14,8 @@ const Abi = enum{
gnu, gnu,
msvc, msvc,
}; };
const CallingConvention = enum{
system_v,
naked,
};

View File

@ -5,13 +5,13 @@ const system = switch (current) {
.windows => windows, .windows => windows,
}; };
const write = fn (file_descriptor: FileDescriptor, bytes_ptr: [@]const u8, bytes_len: usize) ssize { const write = fn (file_descriptor: FileDescriptor, bytes_ptr: [&]const u8, bytes_len: usize) ssize {
switch (current) { switch (current) {
.linux => return #syscall(1, file_descriptor, #cast(bytes_ptr), bytes_len), .linux => return #syscall(1, file_descriptor, #cast(bytes_ptr), bytes_len),
.macos => return macos.write(file_descriptor, #cast(bytes_ptr), bytes_len), .macos => return macos.write(file_descriptor, #cast(bytes_ptr), bytes_len),
.windows => { .windows => {
var written_bytes: u32 = 0; var written_bytes: u32 = 0;
if (windows.WriteFile(file_descriptor, bytes_ptr, bytes_len, @written_bytes, false) != 0) { if (windows.WriteFile(file_descriptor, bytes_ptr, bytes_len, written_bytes.&, false) != 0) {
return written_bytes; return written_bytes;
} else { } else {
unreachable; unreachable;
@ -22,7 +22,7 @@ const write = fn (file_descriptor: FileDescriptor, bytes_ptr: [@]const u8, bytes
const FileDescriptor = system.FileDescriptor; const FileDescriptor = system.FileDescriptor;
const print = fn(bytes_ptr: [@]const u8, bytes_len: usize) void { const print = fn(bytes_ptr: [&]const u8, bytes_len: usize) void {
const file_descriptor = switch (current) { const file_descriptor = switch (current) {
.linux, .macos => 2, .linux, .macos => 2,
.windows => windows.GetStdHandle(windows.STD_OUTPUT_HANDLE), .windows => windows.GetStdHandle(windows.STD_OUTPUT_HANDLE),
@ -41,6 +41,38 @@ const exit = fn(exit_code: s32) noreturn {
unreachable; unreachable;
} }
const ProtectionFlags = struct(u32){
read: bool,
write: bool,
execute: bool,
};
const MapFlags = struct(u32){
reserve: bool,
commit: bool,
};
const allocate_virtual_memory = fn(address: ?[&]u8, length: usize, general_protection_flags: ProtectionFlags, general_map_flags: MapFlags) ?[&]u8 {
const protection_flags = system.get_protection_flags(flags = general_protection_flags);
const map_flags = system.get_map_flags(flags = general_map_flags);
const result = switch (#import("builtin").os) {
.linux => linux.mmap(address, length, protection_flags, map_flags, fd = -1, offset = 0),
else => #error("OS not supported"),
};
return result;
}
const free_virtual_memory = fn(bytes_ptr: [&]const u8, bytes_len: usize) bool {
const result = switch (#import("builtin").os) {
.linux => linux.munmap(bytes_ptr, bytes_len),
else => #error("OS not supported"),
};
return result;
}
const linux = #import("os/linux.nat"); const linux = #import("os/linux.nat");
const macos = #import("os/macos.nat"); const macos = #import("os/macos.nat");
const windows = #import("os/windows.nat"); const windows = #import("os/windows.nat");

View File

@ -1 +1,44 @@
const std = #import("std");
const FileDescriptor = s32; const FileDescriptor = s32;
const ProtectionFlags = struct(u32) {
read: bool,
write: bool,
execute: bool,
};
const MapFlags = struct(u32){
shared: bool,
private: bool,
reserved: u2 = 0,
fixed: bool,
anonymous: bool,
};
const get_protection_flags = fn(flags: std.os.ProtectionFlags) ProtectionFlags {
return ProtectionFlags{
.read = flags.read,
.write = flags.write,
.execute = flags.execute,
};
}
const get_map_flags = fn(flags: std.os.MapFlags) MapFlags{
return MapFlags{
.shared = false,
.private = true,
.fixed = false,
.anonymous = true,
};
}
const mmap = fn(address: ?[&]u8, length: usize, protection_flags: ProtectionFlags, map_flags: MapFlags, fd: s32, offset: u64) ?[&]u8 {
const result = #syscall(9, #cast(address), length, #cast(protection_flags), #cast(map_flags), fd, offset);
return #cast(result);
}
const munmap = fn(bytes_ptr: [&]const u8, bytes_len: usize) bool {
const result: ssize = #syscall(11, #cast(bytes_ptr), bytes_len);
return result == 0;
}

View File

@ -1,3 +1,3 @@
const FileDescriptor = s32; const FileDescriptor = s32;
const write = fn (file_descriptor: FileDescriptor, bytes_ptr: [@]const u8, bytes_len: usize) ssize extern; const write = fn (file_descriptor: FileDescriptor, bytes_ptr: [&]const u8, bytes_len: usize) ssize extern;
const exit = fn (exit_code: u32) noreturn extern; const exit = fn (exit_code: u32) noreturn extern;

View File

@ -3,7 +3,15 @@ comptime {
_ = _start; _ = _start;
} }
const _start = fn () noreturn { const _start = fn () noreturn export cc(.naked) {
#asm({
xor ebp, ebp;
and rsp, 0xfffffffffffffff0;
call {start};
});
}
const start = fn() noreturn export {
const result = #import("main").main(); const result = #import("main").main();
std.os.exit(exit_code = result); std.os.exit(exit_code = result);
} }

View File

@ -6,3 +6,83 @@ const builtin = #import("builtin.nat");
const os = #import("os.nat"); const os = #import("os.nat");
const print = os.print; const print = os.print;
const start = #import("start.nat"); const start = #import("start.nat");
const assert = fn(ok: bool) void {
if (!ok) {
unreachable;
}
}
const Allocator = struct {
handler: &const fn(allocator: &Allocator, old_ptr: ?[&]const u8, old_size: usize, new_size: usize, alignment: u16) ?[&]u8,
const allocate = fn (allocator: &Allocator, size: usize, alignment: u16) ?[]u8 {
if (allocator.handler(allocator, old_ptr = null, old_size = 0, new_size = size, alignment)) |result| {
return result[0..size];
} else {
return null;
}
}
const free = fn (allocator: &Allocator, bytes_ptr: [&]const u8, bytes_len: usize) bool {
if (allocator.handler(allocator, old_ptr = bytes_ptr, old_size = bytes_len, new_size = 0, alignment = 0)) |_| {
return true;
} else {
return false;
}
}
};
const PageAllocator = struct{
allocator: Allocator = .{
.handler = handler.&,
},
const allocate = fn (a: &PageAllocator, size: usize, alignment: u16) ?[]u8 {
const result = a.allocator.allocate(size, alignment);
return result;
}
const free = fn (a: &PageAllocator, bytes_ptr: [&]const u8, bytes_len: usize) bool {
const result = a.allocator.free(bytes_ptr, bytes_len);
return result;
}
const handler = fn (allocator: &Allocator, maybe_old_ptr: ?[&]const u8, old_size: usize, new_size: usize, alignment: u16) ?[&]u8{
var maybe_new_ptr: ?[&]u8 = null;
if (new_size > 0) {
const general_protection_flags = os.ProtectionFlags{
.read = true,
.write = true,
.execute = false,
};
const general_map_flags = os.MapFlags{
.reserve = true,
.commit = true,
};
maybe_new_ptr = os.allocate_virtual_memory(address = null, length = new_size, general_protection_flags, general_map_flags);
}
if (maybe_old_ptr) |old_ptr| {
if (maybe_new_ptr) |new_ptr| {
unreachable;
}
const result = os.free_virtual_memory(bytes_ptr = old_ptr, bytes_len = old_size);
if (result) {
return #cast(old_ptr);
} else {
return null;
}
} else {
return maybe_new_ptr;
}
}
const getAllocator = fn(page_allocator: &PageAllocator) &Allocator {
return page_allocator.allocator.&;
}
};
var page_allocator = PageAllocator{};

19
src/main.nat Normal file
View File

@ -0,0 +1,19 @@
const std = #import("std");
const main = fn() s32 {
const size = 0x1000;
if (std.page_allocator.allocate(size, alignment = 12)) |result| {
result[0] = 0;
std.print(bytes_ptr = "Allocation succeeded. Freeing...\n", bytes_len = 33);
if (std.page_allocator.free(bytes_ptr = result.ptr, bytes_len = result.len)) {
std.print(bytes_ptr = "Memory freed successfully\n", bytes_len = 26);
return 0;
} else {
std.print(bytes_ptr = "Memory freed with errors\n", bytes_len = 25);
return 1;
}
} else {
std.print(bytes_ptr = "Allocation failed!\n", bytes_len = 19);
return 1;
}
}