Constant sub
This commit is contained in:
parent
34a3514856
commit
c4d25ca393
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@ -74,6 +74,7 @@ jobs:
|
||||
version: master
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y llvm-dev liblld-dev
|
||||
if: matrix.os == 'ubuntu-24.04'
|
||||
- name: Build and test (Packaged LLVM)
|
||||
run: |
|
||||
zig build test --summary all -Doptimize=${{matrix.BIRTH_ZIG_BUILD_TYPE}} -Dsystem_llvm=false
|
||||
|
31
src/LLVM.zig
31
src/LLVM.zig
@ -1,7 +1,8 @@
|
||||
const lib = @import("lib.zig");
|
||||
const builtin = @import("builtin");
|
||||
const Arena = lib.Arena;
|
||||
const assert = lib.assert;
|
||||
const os = lib.os;
|
||||
const builtin = @import("builtin");
|
||||
const api = @import("llvm_api.zig");
|
||||
|
||||
/// This is a String which ABI-compatible with C++
|
||||
@ -539,6 +540,13 @@ pub const Builder = opaque {
|
||||
pub const position_at_end = api.LLVMPositionBuilderAtEnd;
|
||||
|
||||
pub const create_ret = api.LLVMBuildRet;
|
||||
pub fn create_add(builder: *Builder, left: *Value, right: *Value) *Value {
|
||||
return api.LLVMBuildAdd(builder, left, right, "");
|
||||
}
|
||||
|
||||
pub fn create_sub(builder: *Builder, left: *Value, right: *Value) *Value {
|
||||
return api.LLVMBuildSub(builder, left, right, "");
|
||||
}
|
||||
|
||||
pub fn create_ret_void(builder: *Builder) void {
|
||||
builder.create_ret(null);
|
||||
@ -695,6 +703,7 @@ pub const lld = struct {
|
||||
|
||||
pub const Thread = struct {
|
||||
context: *Context,
|
||||
builder: *Builder,
|
||||
i1: Integer,
|
||||
i8: Integer,
|
||||
i16: Integer,
|
||||
@ -724,6 +733,7 @@ pub const Thread = struct {
|
||||
|
||||
thread.* = .{
|
||||
.context = context,
|
||||
.builder = context.create_builder(),
|
||||
.i1 = .{
|
||||
.type = type_i1,
|
||||
.zero = zero_i1,
|
||||
@ -793,21 +803,6 @@ const LldArgvBuilder = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const ModuleBuilder = struct {
|
||||
module: *Module,
|
||||
builder: *Builder,
|
||||
|
||||
pub fn initialize(thread: *Thread, module_name: []const u8) ModuleBuilder {
|
||||
const module = thread.context.create_module(module_name);
|
||||
const builder = thread.context.create_builder();
|
||||
|
||||
return .{
|
||||
.module = module,
|
||||
.builder = builder,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const FunctionBuilder = struct {
|
||||
function: *Function,
|
||||
current_basic_block: *BasicBlock,
|
||||
@ -901,7 +896,9 @@ pub fn link(arena: *Arena, options: LinkOptions) lld.Result {
|
||||
const dynamic_linker = true;
|
||||
if (dynamic_linker) {
|
||||
arg_builder.add("-dynamic-linker");
|
||||
arg_builder.add("/usr/lib/ld-linux-x86-64.so.2");
|
||||
|
||||
const dynamic_linker_path = "/usr/lib64/ld-linux-x86-64.so.2";
|
||||
arg_builder.add(dynamic_linker_path);
|
||||
}
|
||||
|
||||
if (link_libc) {
|
||||
|
@ -75,8 +75,7 @@ const Converter = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_type(noalias converter: *Converter, noalias thread: *llvm.Thread, noalias module_builder: *llvm.ModuleBuilder) *llvm.Type {
|
||||
_ = module_builder;
|
||||
pub fn parse_type(noalias converter: *Converter, noalias thread: *llvm.Thread) *llvm.Type {
|
||||
const identifier = converter.parse_identifier();
|
||||
var integer_type = identifier.len > 1 and identifier[0] == 's' or identifier[0] == 'u';
|
||||
if (integer_type) {
|
||||
@ -150,6 +149,10 @@ const Converter = struct {
|
||||
const integer_start_ch = converter.content[start];
|
||||
assert(!is_space(integer_start_ch));
|
||||
assert(is_decimal_ch(integer_start_ch));
|
||||
const integer_type = expected_type.to_integer();
|
||||
|
||||
const sign_extend = false;
|
||||
var value: u64 = 0;
|
||||
|
||||
switch (integer_start_ch) {
|
||||
'0' => {
|
||||
@ -172,18 +175,25 @@ const Converter = struct {
|
||||
converter.report_error();
|
||||
},
|
||||
// Zero literal
|
||||
else => {
|
||||
const integer_type = expected_type.to_integer();
|
||||
const sign_extend = false;
|
||||
const integer_value = integer_type.get_constant(0, @intFromBool(sign_extend));
|
||||
return integer_value.to_value();
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
'1'...'9' => {
|
||||
while (true) {
|
||||
const ch = converter.content[converter.offset];
|
||||
if (!is_decimal_ch(ch)) {
|
||||
break;
|
||||
}
|
||||
|
||||
converter.offset += 1;
|
||||
value = lib.parse.accumulate_decimal(value, ch);
|
||||
}
|
||||
},
|
||||
// TODO: decimal number
|
||||
'1'...'9' => converter.report_error(),
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
const integer_value = integer_type.get_constant(value, @intFromBool(sign_extend));
|
||||
return integer_value.to_value();
|
||||
}
|
||||
|
||||
fn expect_character(noalias converter: *Converter, expected_ch: u8) void {
|
||||
@ -192,8 +202,7 @@ const Converter = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_block(noalias converter: *Converter, thread: *llvm.Thread, module_builder: *llvm.ModuleBuilder, function_builder: *llvm.FunctionBuilder) void {
|
||||
_ = thread;
|
||||
fn parse_block(noalias converter: *Converter, noalias thread: *llvm.Thread, noalias function_builder: *llvm.FunctionBuilder) void {
|
||||
converter.skip_space();
|
||||
|
||||
converter.expect_character(left_brace);
|
||||
@ -216,8 +225,8 @@ const Converter = struct {
|
||||
if (string_to_enum(StatementStartKeyword, statement_start_identifier)) |statement_start_keyword| {
|
||||
switch (statement_start_keyword) {
|
||||
.@"return" => {
|
||||
const return_value = converter.parse_value(function_builder.function.get_type().get_return_type());
|
||||
module_builder.builder.create_ret(return_value);
|
||||
const return_value = converter.parse_value(thread, function_builder.function.get_type().get_return_type());
|
||||
thread.builder.create_ret(return_value);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -239,7 +248,51 @@ const Converter = struct {
|
||||
converter.expect_character(right_brace);
|
||||
}
|
||||
|
||||
fn parse_value(noalias converter: *Converter, expected_type: *llvm.Type) *llvm.Value {
|
||||
const ExpressionState = enum {
|
||||
none,
|
||||
sub,
|
||||
};
|
||||
|
||||
fn parse_value(noalias converter: *Converter, noalias thread: *llvm.Thread, expected_type: *llvm.Type) *llvm.Value {
|
||||
converter.skip_space();
|
||||
|
||||
var value_state = ExpressionState.none;
|
||||
var previous_value: *llvm.Value = undefined;
|
||||
|
||||
const value = while (true) {
|
||||
const current_value = switch (converter.content[converter.offset] == left_parenthesis) {
|
||||
true => os.abort(),
|
||||
false => converter.parse_single_value(expected_type),
|
||||
};
|
||||
|
||||
converter.skip_space();
|
||||
|
||||
switch (value_state) {
|
||||
.none => previous_value = current_value,
|
||||
.sub => {
|
||||
const left = previous_value;
|
||||
const right = current_value;
|
||||
previous_value = thread.builder.create_sub(left, right);
|
||||
},
|
||||
}
|
||||
|
||||
const ch = converter.content[converter.offset];
|
||||
value_state = switch (ch) {
|
||||
';' => break previous_value,
|
||||
'-' => blk: {
|
||||
converter.offset += 1;
|
||||
break :blk .sub;
|
||||
},
|
||||
else => os.abort(),
|
||||
};
|
||||
|
||||
converter.skip_space();
|
||||
};
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
fn parse_single_value(noalias converter: *Converter, expected_type: *llvm.Type) *llvm.Value {
|
||||
converter.skip_space();
|
||||
|
||||
const start = converter.offset;
|
||||
@ -299,6 +352,81 @@ pub const BuildMode = enum {
|
||||
}
|
||||
};
|
||||
|
||||
fn invoke(name: []const u8) !void {
|
||||
if (!lib.GlobalState.initialized) {
|
||||
lib.GlobalState.initialize();
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
comptime assert(lib.is_test);
|
||||
const allocator = std.testing.allocator;
|
||||
|
||||
inline for (@typeInfo(BuildMode).@"enum".fields) |f| {
|
||||
const build_mode = @field(BuildMode, f.name);
|
||||
inline for ([2]u1{ 0, 1 }) |has_debug_info| {
|
||||
var tmp_dir = std.testing.tmpDir(.{});
|
||||
defer tmp_dir.cleanup();
|
||||
const base_path = lib.global.arena.join_string(&.{ ".zig-cache/tmp/", &tmp_dir.sub_path, "/", name });
|
||||
const executable_path = base_path;
|
||||
invoke_wrapper(.{
|
||||
.object_path = lib.global.arena.join_string(&.{ base_path, ".o" }),
|
||||
.executable_path = executable_path,
|
||||
.file_path = lib.global.arena.join_string(&.{ "tests/", name, ".bbb" }),
|
||||
.name = name,
|
||||
}, build_mode, has_debug_info);
|
||||
const run_result = std.process.Child.run(.{
|
||||
.allocator = allocator,
|
||||
.argv = &.{executable_path},
|
||||
}) catch |err| {
|
||||
std.debug.print("error: {}\n", .{err});
|
||||
const path = lib.global.arena.join_string(&.{ ".zig-cache/tmp/", &tmp_dir.sub_path });
|
||||
const r = try std.process.Child.run(.{
|
||||
.allocator = allocator,
|
||||
.argv = &.{ "/usr/bin/ls", "-lasR", path },
|
||||
.max_output_bytes = std.math.maxInt(usize),
|
||||
});
|
||||
defer allocator.free(r.stdout);
|
||||
defer allocator.free(r.stderr);
|
||||
std.debug.print("ls {s} {s}\n", .{ path, r.stdout });
|
||||
return err;
|
||||
};
|
||||
const success = switch (run_result.term) {
|
||||
.Exited => |exit_code| exit_code == 0,
|
||||
else => false,
|
||||
};
|
||||
if (!success) {
|
||||
return error.executable_failed_to_run_successfully;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const InvokeWrapper = struct {
|
||||
executable_path: [:0]const u8,
|
||||
object_path: [:0]const u8,
|
||||
file_path: [:0]const u8,
|
||||
name: []const u8,
|
||||
};
|
||||
|
||||
// We invoke a function with comptime parameters so it's easily visible in CI stack traces
|
||||
fn invoke_wrapper(options: InvokeWrapper, comptime build_mode: BuildMode, comptime has_debug_info: u1) void {
|
||||
return invoke_single(options, build_mode, has_debug_info);
|
||||
}
|
||||
|
||||
fn invoke_single(options: InvokeWrapper, build_mode: BuildMode, has_debug_info: u1) void {
|
||||
const file_content = lib.file.read(lib.global.arena, options.file_path);
|
||||
|
||||
convert(.{
|
||||
.path = options.file_path,
|
||||
.content = file_content,
|
||||
.object = options.object_path,
|
||||
.executable = options.executable_path,
|
||||
.build_mode = build_mode,
|
||||
.name = options.name,
|
||||
.has_debug_info = has_debug_info,
|
||||
});
|
||||
}
|
||||
|
||||
const ConvertOptions = struct {
|
||||
content: []const u8,
|
||||
path: [:0]const u8,
|
||||
@ -316,8 +444,7 @@ pub noinline fn convert(options: ConvertOptions) void {
|
||||
};
|
||||
|
||||
const thread = llvm.default_initialize();
|
||||
var module_builder_memory = llvm.ModuleBuilder.initialize(thread, options.name);
|
||||
const module_builder = &module_builder_memory;
|
||||
const module = thread.context.create_module(options.name);
|
||||
|
||||
while (true) {
|
||||
converter.skip_space();
|
||||
@ -418,10 +545,10 @@ pub noinline fn convert(options: ConvertOptions) void {
|
||||
|
||||
converter.skip_space();
|
||||
|
||||
const return_type = converter.parse_type(thread, module_builder);
|
||||
const return_type = converter.parse_type(thread);
|
||||
const function_type = llvm.Type.Function.get(return_type, &.{}, false);
|
||||
|
||||
const function = module_builder.module.create_function(.{
|
||||
const function = module.create_function(.{
|
||||
.name = global_name,
|
||||
.linkage = switch (is_export) {
|
||||
true => .ExternalLinkage,
|
||||
@ -431,14 +558,14 @@ pub noinline fn convert(options: ConvertOptions) void {
|
||||
});
|
||||
|
||||
const entry_block = thread.context.create_basic_block("entry", function);
|
||||
module_builder.builder.position_at_end(entry_block);
|
||||
thread.builder.position_at_end(entry_block);
|
||||
|
||||
var function_builder = llvm.FunctionBuilder{
|
||||
.function = function,
|
||||
.current_basic_block = entry_block,
|
||||
};
|
||||
|
||||
converter.parse_block(thread, module_builder, &function_builder);
|
||||
converter.parse_block(thread, &function_builder);
|
||||
|
||||
if (lib.optimization_mode == .Debug) {
|
||||
const verify_result = function.verify();
|
||||
@ -452,13 +579,13 @@ pub noinline fn convert(options: ConvertOptions) void {
|
||||
}
|
||||
|
||||
if (lib.optimization_mode == .Debug) {
|
||||
const verify_result = module_builder.module.verify();
|
||||
const verify_result = module.verify();
|
||||
if (!verify_result.success) {
|
||||
os.abort();
|
||||
}
|
||||
|
||||
if (!lib.is_test) {
|
||||
const module_string = module_builder.module.to_string();
|
||||
const module_string = module.to_string();
|
||||
lib.print_string_stderr(module_string);
|
||||
}
|
||||
}
|
||||
@ -477,7 +604,7 @@ pub noinline fn convert(options: ConvertOptions) void {
|
||||
os.abort();
|
||||
};
|
||||
|
||||
const object_generate_result = llvm.object_generate(module_builder.module, target_machine, .{
|
||||
const object_generate_result = llvm.object_generate(module, target_machine, .{
|
||||
.optimize_when_possible = @intFromBool(@intFromEnum(options.build_mode) > @intFromEnum(BuildMode.soft_optimize)),
|
||||
.debug_info = options.has_debug_info,
|
||||
.optimization_level = if (options.build_mode != .debug_none) options.build_mode.to_llvm_ir() else null,
|
||||
@ -501,47 +628,9 @@ pub noinline fn convert(options: ConvertOptions) void {
|
||||
}
|
||||
|
||||
test "minimal" {
|
||||
invoke("minimal");
|
||||
try invoke("minimal");
|
||||
}
|
||||
|
||||
fn invoke(name: []const u8) void {
|
||||
inline for (@typeInfo(BuildMode).@"enum".fields) |f| {
|
||||
const build_mode = @field(BuildMode, f.name);
|
||||
inline for ([2]u1{ 0, 1 }) |has_debug_info| {
|
||||
invoke_wrapper(name, build_mode, has_debug_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We invoke a function with comptime parameters so it's easily visible in CI stack traces
|
||||
fn invoke_wrapper(name: []const u8, comptime build_mode: BuildMode, comptime has_debug_info: u1) void {
|
||||
return invoke_single(name, build_mode, has_debug_info);
|
||||
}
|
||||
|
||||
fn invoke_single(name: []const u8, build_mode: BuildMode, has_debug_info: u1) void {
|
||||
const std = @import("std");
|
||||
if (!lib.GlobalState.initialized) {
|
||||
lib.GlobalState.initialize();
|
||||
}
|
||||
var tmp_dir = if (lib.is_test) std.testing.tmpDir(.{}) else {};
|
||||
defer {
|
||||
if (lib.is_test) {
|
||||
tmp_dir.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
const object_path = lib.global.arena.join_string(if (lib.is_test) &.{ ".zig-cache/tmp/", &tmp_dir.sub_path, "/", name, ".o" } else &.{ name, ".o" });
|
||||
const executable_path = lib.global.arena.join_string(if (lib.is_test) &.{ ".zig-cache/tmp/", &tmp_dir.sub_path, "/", name, ".o" } else &.{name});
|
||||
const file_path = lib.global.arena.join_string(&.{ "tests/", name, ".bbb" });
|
||||
const file_content = lib.file.read(lib.global.arena, file_path);
|
||||
|
||||
convert(.{
|
||||
.path = file_path,
|
||||
.content = file_content,
|
||||
.object = object_path,
|
||||
.executable = executable_path,
|
||||
.build_mode = build_mode,
|
||||
.name = name,
|
||||
.has_debug_info = has_debug_info,
|
||||
});
|
||||
test "constant sub" {
|
||||
try invoke("constant_sub");
|
||||
}
|
||||
|
@ -2492,11 +2492,15 @@ pub const parse = struct {
|
||||
|
||||
for (str) |ch| {
|
||||
assert(is_decimal_digit(ch));
|
||||
value = (value * 10) + (ch - '0');
|
||||
value = accumulate_decimal(value, ch);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
pub fn accumulate_decimal(accumulator: u64, ch: u8) u64 {
|
||||
return (accumulator * 10) + (ch - '0');
|
||||
}
|
||||
};
|
||||
|
||||
fn vprint(format_string: [*:0]const u8, args: *VariableArguments) void {
|
||||
|
@ -19,6 +19,8 @@ pub extern fn llvm_module_to_string(module: *llvm.Module) llvm.String;
|
||||
// Builder API
|
||||
pub extern fn LLVMPositionBuilderAtEnd(builder: *llvm.Builder, basic_block: *llvm.BasicBlock) void;
|
||||
pub extern fn LLVMBuildRet(builder: *llvm.Builder, value: ?*llvm.Value) void;
|
||||
pub extern fn LLVMBuildAdd(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value;
|
||||
pub extern fn LLVMBuildSub(builder: *llvm.Builder, left: *llvm.Value, right: *llvm.Value, name: [*:0]const u8) *llvm.Value;
|
||||
|
||||
pub extern fn LLVMTypeOf(value: *llvm.Value) *llvm.Type;
|
||||
pub extern fn LLVMGlobalGetValueType(value: *llvm.GlobalValue) *llvm.Type;
|
||||
|
5
tests/constant_sub.bbb
Normal file
5
tests/constant_sub.bbb
Normal file
@ -0,0 +1,5 @@
|
||||
[export] main = fn [cc(c)] () s32
|
||||
{
|
||||
return 1 - 1;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user