diff --git a/bootstrap/backend/llvm_bindings.zig b/bootstrap/backend/llvm_bindings.zig index 001801f..9bb9c15 100644 --- a/bootstrap/backend/llvm_bindings.zig +++ b/bootstrap/backend/llvm_bindings.zig @@ -123,6 +123,7 @@ pub extern fn NativityLLVMGetStruct(struct_type: *LLVM.Type.Struct, constant_ptr pub extern fn NativityLLVMValueToConstant(value: *LLVM.Value) ?*LLVM.Value.Constant; pub extern fn NativityLLVMValueToFunction(value: *LLVM.Value) ?*LLVM.Value.Constant.Function; +pub extern fn NativityLLVMValueToBasicBlock(value: *LLVM.Value) ?*LLVM.Value.BasicBlock; pub extern fn NativityLLVMTypeIsPointer(type: *LLVM.Type) bool; pub extern fn NativityLLVMTypeIsInteger(type: *LLVM.Type) bool; diff --git a/bootstrap/compiler.zig b/bootstrap/compiler.zig index dd7b01c..b476e25 100644 --- a/bootstrap/compiler.zig +++ b/bootstrap/compiler.zig @@ -457,6 +457,50 @@ const Parser = struct{ if (analyzer.current_scope.get_declaration(identifier)) |lookup_result| { switch (src[parser.i]) { + '(' => { + parser.i += 1; + parser.skip_space(src); + + switch (lookup_result.declaration.*.id) { + .local => unreachable, + .global => { + const global = lookup_result.declaration.*.get_payload(.global); + switch (global.id) { + .function_definition => { + const function_definition = global.get_payload(.function_definition); + const declaration_argument_count = function_definition.declaration.argument_types.len; + const argument_count: u32 = 0; + while (src[parser.i] != ')') { + unreachable; + } + parser.i += 1; + + if (declaration_argument_count != argument_count) { + exit(1); + } + + const call = thread.calls.append(.{ + .instruction = .{ + .value = .{ + .sema = .{ + .thread = thread.get_index(), + .resolved = true, + .id = .instruction, + }, + }, + .id = .call, + }, + .callable = &function_definition.declaration.value, + }); + _ = analyzer.current_basic_block.instructions.append(&call.instruction); + return &call.instruction.value; + }, + else => |t| @panic(@tagName(t)), + } + }, + } + + }, '.' => { switch (lookup_result.declaration.*.id) { .global => { @@ -560,6 +604,7 @@ const Parser = struct{ const CurrentOperation = enum{ none, + assign, add, add_assign, sub, @@ -582,6 +627,8 @@ const Parser = struct{ arithmetic_shift_right_assign, logical_shift_right, logical_shift_right_assign, + + compare_equal, }; fn parse_expression(parser: *Parser, analyzer: *Analyzer, thread: *Thread, file: *File, ty: ?*Type, side: Side) *Value { @@ -589,14 +636,19 @@ const Parser = struct{ var current_operation = CurrentOperation.none; var previous_value: *Value = undefined; + var iterations: usize = 0; + var it_ty: ?*Type = ty; while (true) { + if (iterations == 1 and it_ty == null) { + it_ty = previous_value.get_type(); + } var current_value: *Value = undefined; if (src[parser.i] == '(') { parser.i += 1; - current_value = parser.parse_expression(analyzer, thread, file, ty, side); + current_value = parser.parse_expression(analyzer, thread, file, it_ty, side); parser.expect_character(src, ')'); } else { - current_value = parser.parse_single_expression(analyzer, thread, file, ty, side); + current_value = parser.parse_single_expression(analyzer, thread, file, it_ty, side); } parser.skip_space(src); @@ -605,8 +657,34 @@ const Parser = struct{ .none => { previous_value = current_value; }, + .compare_equal => { + switch (current_operation) { + else => unreachable, + inline .compare_equal => |co| { + const string = @tagName(co)["compare_".len..]; + const comparison = @field(IntegerCompare.Id, string); + const compare = thread.integer_compares.append(.{ + .instruction = .{ + .value = .{ + .sema = .{ + .thread = thread.get_index(), + .resolved = true, + .id = .instruction, + }, + }, + .id = .integer_compare, + }, + .left = previous_value, + .right = current_value, + .id = comparison, + }); + _ = analyzer.current_basic_block.instructions.append(&compare.instruction); + previous_value = &compare.instruction.value; + } + } + }, .add, .sub, .mul, .udiv, .sdiv, .@"and", .@"or", .xor, .shift_left, .arithmetic_shift_right, .logical_shift_right => { - const add = thread.integer_binary_operations.append(.{ + const i = thread.integer_binary_operations.append(.{ .instruction = .{ .value = .{ .sema = .{ @@ -620,19 +698,33 @@ const Parser = struct{ .left = previous_value, .right = current_value, .id = switch (current_operation) { - .none, .add_assign, .sub_assign, .mul_assign, .udiv_assign, .sdiv_assign, .and_assign, .or_assign, .xor_assign, .shift_left_assign, .arithmetic_shift_right_assign, .logical_shift_right_assign => unreachable, + .none, .assign, .add_assign, .sub_assign, .mul_assign, .udiv_assign, .sdiv_assign, .and_assign, .or_assign, .xor_assign, .shift_left_assign, .arithmetic_shift_right_assign, .logical_shift_right_assign, .compare_equal => unreachable, inline else => |co| @field(IntegerBinaryOperation.Id, @tagName(co)), }, - .type = if (ty) |t| t else current_value.get_type(), + .type = if (it_ty) |t| t else current_value.get_type(), }); - _ = analyzer.current_basic_block.instructions.append(&add.instruction); - previous_value = &add.instruction.value; + _ = analyzer.current_basic_block.instructions.append(&i.instruction); + previous_value = &i.instruction.value; }, - .add_assign, .sub_assign, .mul_assign, .udiv_assign, .sdiv_assign, .and_assign, .or_assign, .xor_assign, .shift_left_assign, .logical_shift_right_assign, .arithmetic_shift_right_assign => unreachable, + .assign, .add_assign, .sub_assign, .mul_assign, .udiv_assign, .sdiv_assign, .and_assign, .or_assign, .xor_assign, .shift_left_assign, .logical_shift_right_assign, .arithmetic_shift_right_assign => unreachable, } switch (src[parser.i]) { ')', ';' => return previous_value, + '=' => { + current_operation = .assign; + parser.i += 1; + + switch (src[parser.i]) { + '=' => { + current_operation = .compare_equal; + parser.i += 1; + }, + else => {}, + } + + parser.skip_space(src); + }, '+' => { current_operation = .add; parser.i += 1; @@ -676,7 +768,7 @@ const Parser = struct{ parser.skip_space(src); }, '/' => { - const int_ty = ty orelse previous_value.get_type(); + const int_ty = it_ty orelse previous_value.get_type(); const integer_type = int_ty.get_payload(.integer); current_operation = switch (integer_type.signedness) { .unsigned => .udiv, @@ -769,7 +861,7 @@ const Parser = struct{ switch (src[parser.i]) { '>' => { - const int_ty = ty orelse unreachable; + const int_ty = it_ty orelse unreachable; const integer_type = int_ty.get_payload(.integer); current_operation = switch (integer_type.signedness) { .unsigned => .logical_shift_right, @@ -796,6 +888,8 @@ const Parser = struct{ }, else => @panic((src.ptr + parser.i)[0..1]), } + + iterations += 1; } } }; @@ -869,12 +963,15 @@ const Value = struct { reserved: u32 = 0, const Id = enum(u8){ + basic_block, constant_int, lazy_expression, instruction, function_declaration, }; + const id_to_value_map = std.EnumArray(Id, type).init(.{ + .basic_block = BasicBlock, .constant_int = ConstantInt, .function_declaration = Function.Declaration, .instruction = Instruction, @@ -899,9 +996,26 @@ const Value = struct { const load = instruction.get_payload(.load); break :block load.type; }, + .call => { + const call = instruction.get_payload(.call); + switch (call.callable.sema.id) { + .function_declaration => { + const function_declaration = call.callable.get_payload(.function_declaration); + return function_declaration.return_type; + }, + else => |t| @panic(@tagName(t)), + } + }, + .integer_compare => { + return &instance.threads[value.sema.thread].integers[0].type; + }, else => |t| @panic(@tagName(t)), }; }, + .constant_int => { + const constant_int = value.get_payload(.constant_int); + return constant_int.type; + }, else => |t| @panic(@tagName(t)), }; } @@ -949,7 +1063,9 @@ const Type = struct { }; const Keyword = enum{ + @"else", @"for", + @"if", }; fn parse_keyword(identifier: []const u8) u32 { @@ -1052,11 +1168,21 @@ const GlobalDeclaration = struct { }; const BasicBlock = struct{ + value: Value, instructions: PinnedArray(*Instruction) = .{}, - predecessors: PinnedArray(u32) = .{}, + predecessors: PinnedArray(*BasicBlock) = .{}, is_terminated: bool = false, + command_node: CommandList.Node = .{ + .data = {}, + }, + + const CommandList = std.DoublyLinkedList(void); const Index = PinnedArray(BasicBlock).Index; + + pub fn get_llvm(basic_block: *BasicBlock) *LLVM.Value.BasicBlock{ + return basic_block.value.llvm.?.toBasicBlock() orelse unreachable; + } }; const Declaration = struct { @@ -1083,7 +1209,7 @@ const Declaration = struct { const Function = struct{ declaration: Function.Declaration, - entry_block: BasicBlock.Index, + entry_block: *BasicBlock, stack_slots: PinnedArray(*LocalSymbol) = .{}, scope: Function.Scope, @@ -1128,7 +1254,9 @@ const Instruction = struct{ id: Id, const Id = enum{ + branch, integer_binary_operation, + integer_compare, call, load, local_symbol, @@ -1138,8 +1266,10 @@ const Instruction = struct{ }; const id_to_instruction_map = std.EnumArray(Id, type).init(.{ + .branch = Branch, .call = Call, .integer_binary_operation = IntegerBinaryOperation, + .integer_compare = IntegerCompare, .local_symbol = LocalSymbol, .load = Load, .ret = Return, @@ -1175,6 +1305,26 @@ const IntegerBinaryOperation = struct { }; }; +const IntegerCompare = struct { + instruction: Instruction, + left: *Value, + right: *Value, + // type: *Type, + id: Id, + + const Id = enum{ + equal, + not_equal, + }; +}; + +const Branch = struct { + instruction: Instruction, + condition: *Value, + taken: *BasicBlock, + not_taken: *BasicBlock, +}; + const Call = struct{ instruction: Instruction, callable: *Value, @@ -1232,8 +1382,10 @@ const Thread = struct{ task_system: TaskSystem = .{}, debug_info_file_map: PinnedHashMap(u32, LLVMFile) = .{}, // pending_values_per_file: PinnedArray(PinnedArray(*Value)) = .{}, + branches: PinnedArray(Branch) = .{}, calls: PinnedArray(Call) = .{}, integer_binary_operations: PinnedArray(IntegerBinaryOperation) = .{}, + integer_compares: PinnedArray(IntegerCompare) = .{}, loads: PinnedArray(Load) = .{}, stores: PinnedArray(Store) = .{}, returns: PinnedArray(Return) = .{}, @@ -2373,79 +2525,134 @@ fn worker_thread(thread_index: u32, cpu_count: *u32) void { for (thread.functions.slice()) |*nat_function| { const function = nat_function.declaration.value.llvm.?.toFunction() orelse unreachable; - const nat_entry_basic_block = thread.basic_blocks.get(nat_function.entry_block); - assert(nat_entry_basic_block.predecessors.length == 0); - const entry_block_name = "entry"; - const entry_block = thread.llvm.context.createBasicBlock(entry_block_name, entry_block_name.len, function, null); - thread.llvm.builder.setInsertPoint(entry_block); + var basic_block_command_buffer = BasicBlock.CommandList{}; + var emit_allocas = true; + { + const nat_entry_basic_block = nat_function.entry_block; + assert(nat_entry_basic_block.predecessors.length == 0); + const entry_block_name = "entry"; + const entry_block = thread.llvm.context.createBasicBlock(entry_block_name, entry_block_name.len, function, null); + nat_entry_basic_block.value.llvm = entry_block.toValue(); - for (nat_function.stack_slots.slice()) |local_slot| { - const alloca_type = llvm_get_type(thread, local_slot.type); - local_slot.instruction.value.llvm = thread.llvm.builder.createAlloca(alloca_type, address_space, null, "", "".len, local_slot.alignment).toValue(); + basic_block_command_buffer.append(&nat_entry_basic_block.command_node); } - for (nat_entry_basic_block.instructions.slice()) |instruction| { - const value: *LLVM.Value = switch (instruction.id) { - .store => block: { - const store = instruction.get_payload(.store); - const destination = llvm_get_value(thread, store.destination); - const source = llvm_get_value(thread, store.source); - const store_instruction = builder.createStore(source, destination, store.is_volatile, store.alignment); - break :block store_instruction.toValue(); - }, - .load => block: { - const load = instruction.get_payload(.load); - const load_value = llvm_get_value(thread, load.value); - const load_type = llvm_get_type(thread, load.type); - // TODO: precise alignment - const load_instruction = builder.createLoad(load_type, load_value, load.is_volatile, "", "".len, load.alignment); - break :block load_instruction.toValue(); - }, - .ret => block: { - const return_instruction = instruction.get_payload(.ret); - const return_value = llvm_get_value(thread, return_instruction.value); - const ret = thread.llvm.builder.createRet(return_value); - break :block ret.toValue(); - }, - .integer_binary_operation => block: { - const integer_binary_operation = instruction.get_payload(.integer_binary_operation); - const left = llvm_get_value(thread, integer_binary_operation.left); - const right = llvm_get_value(thread, integer_binary_operation.right); - const integer_type = integer_binary_operation.type.get_payload(.integer); - const no_unsigned_wrapping = integer_type.signedness == .unsigned; - const no_signed_wrapping = integer_type.signedness == .signed; - const name = ""; - const is_exact = false; - break :block switch (integer_binary_operation.id) { - .add => builder.createAdd(left, right, name, name.len, no_unsigned_wrapping, no_signed_wrapping), - .sub => builder.createSub(left, right, name, name.len, no_unsigned_wrapping, no_signed_wrapping), - .mul => builder.createMultiply(left, right, name, name.len, no_unsigned_wrapping, no_signed_wrapping), - .udiv => builder.createUDiv(left, right, name, name.len, is_exact), - .sdiv => builder.createSDiv(left, right, name, name.len, is_exact), - .@"and" => builder.createAnd(left, right, name, name.len), - .@"or" => builder.createOr(left, right, name, name.len), - .@"xor" => builder.createXor(left, right, name, name.len), - .shift_left => builder.createShiftLeft(left, right, name, name.len, no_unsigned_wrapping, no_signed_wrapping), - .arithmetic_shift_right => builder.createArithmeticShiftRight(left, right, name, name.len, is_exact), - .logical_shift_right => builder.createLogicalShiftRight(left, right, name, name.len, is_exact), - }; - }, - .call => block: { - const call = instruction.get_payload(.call); - const callee = llvm_get_value(thread, call.callable); - const callee_function = callee.toFunction() orelse unreachable; - const function_type = callee_function.getType(); - const arguments: []const *LLVM.Value = &.{}; - const call_i = thread.llvm.builder.createCall(function_type, callee, arguments.ptr, arguments.len, "", "".len, null); - break :block call_i.toValue(); - }, - else => |t| @panic(@tagName(t)), - }; + while (basic_block_command_buffer.len != 0) { + const basic_block_node = basic_block_command_buffer.first orelse unreachable; + const basic_block: *BasicBlock = @fieldParentPtr("command_node", basic_block_node); + const llvm_basic_block = basic_block.get_llvm(); + thread.llvm.builder.setInsertPoint(llvm_basic_block); - instruction.value.llvm = value; + var last_block = basic_block_node; + + if (emit_allocas) { + for (nat_function.stack_slots.slice()) |local_slot| { + const alloca_type = llvm_get_type(thread, local_slot.type); + local_slot.instruction.value.llvm = thread.llvm.builder.createAlloca(alloca_type, address_space, null, "", "".len, local_slot.alignment).toValue(); + } + emit_allocas = false; + } + + for (basic_block.instructions.slice()) |instruction| { + const value: *LLVM.Value = switch (instruction.id) { + .store => block: { + const store = instruction.get_payload(.store); + const destination = llvm_get_value(thread, store.destination); + const source = llvm_get_value(thread, store.source); + const store_instruction = builder.createStore(source, destination, store.is_volatile, store.alignment); + break :block store_instruction.toValue(); + }, + .load => block: { + const load = instruction.get_payload(.load); + const load_value = llvm_get_value(thread, load.value); + const load_type = llvm_get_type(thread, load.type); + // TODO: precise alignment + const load_instruction = builder.createLoad(load_type, load_value, load.is_volatile, "", "".len, load.alignment); + break :block load_instruction.toValue(); + }, + .ret => block: { + const return_instruction = instruction.get_payload(.ret); + const return_value = llvm_get_value(thread, return_instruction.value); + const ret = thread.llvm.builder.createRet(return_value); + break :block ret.toValue(); + }, + .integer_binary_operation => block: { + const integer_binary_operation = instruction.get_payload(.integer_binary_operation); + const left = llvm_get_value(thread, integer_binary_operation.left); + const right = llvm_get_value(thread, integer_binary_operation.right); + const integer_type = integer_binary_operation.type.get_payload(.integer); + const no_unsigned_wrapping = integer_type.signedness == .unsigned; + const no_signed_wrapping = integer_type.signedness == .signed; + const name = ""; + const is_exact = false; + break :block switch (integer_binary_operation.id) { + .add => builder.createAdd(left, right, name, name.len, no_unsigned_wrapping, no_signed_wrapping), + .sub => builder.createSub(left, right, name, name.len, no_unsigned_wrapping, no_signed_wrapping), + .mul => builder.createMultiply(left, right, name, name.len, no_unsigned_wrapping, no_signed_wrapping), + .udiv => builder.createUDiv(left, right, name, name.len, is_exact), + .sdiv => builder.createSDiv(left, right, name, name.len, is_exact), + .@"and" => builder.createAnd(left, right, name, name.len), + .@"or" => builder.createOr(left, right, name, name.len), + .@"xor" => builder.createXor(left, right, name, name.len), + .shift_left => builder.createShiftLeft(left, right, name, name.len, no_unsigned_wrapping, no_signed_wrapping), + .arithmetic_shift_right => builder.createArithmeticShiftRight(left, right, name, name.len, is_exact), + .logical_shift_right => builder.createLogicalShiftRight(left, right, name, name.len, is_exact), + }; + }, + .call => block: { + const call = instruction.get_payload(.call); + const callee = llvm_get_value(thread, call.callable); + const callee_function = callee.toFunction() orelse unreachable; + const function_type = callee_function.getType(); + + const arguments: []const *LLVM.Value = &.{}; + const call_i = thread.llvm.builder.createCall(function_type, callee, arguments.ptr, arguments.len, "", "".len, null); + break :block call_i.toValue(); + }, + .integer_compare => block: { + const compare = instruction.get_payload(.integer_compare); + const type_a = compare.left.get_type(); + const type_b = compare.right.get_type(); + assert(type_a == type_b); + const left = llvm_get_value(thread, compare.left); + const right = llvm_get_value(thread, compare.right); + const name = ""; + const comparison: LLVM.Value.Instruction.ICmp.Kind = switch (compare.id) { + .equal => .eq, + .not_equal => .ne, + }; + + const cmp = builder.createICmp(comparison, left, right, name, name.len); + break :block cmp; + }, + .branch => block: { + const branch = instruction.get_payload(.branch); + basic_block_command_buffer.insertAfter(last_block, &branch.taken.command_node); + basic_block_command_buffer.insertAfter(&branch.taken.command_node, &branch.not_taken.command_node); + last_block = &branch.not_taken.command_node; + + const taken = thread.llvm.context.createBasicBlock("", "".len, function, null); + branch.taken.value.llvm = taken.toValue(); + const not_taken = thread.llvm.context.createBasicBlock("", "".len, function, null); + branch.not_taken.value.llvm = not_taken.toValue(); + + const condition = llvm_get_value(thread, branch.condition); + const branch_weights = null; + const unpredictable = null; + const br = builder.createConditionalBranch(condition, taken, not_taken, branch_weights, unpredictable); + break :block br.toValue(); + }, + else => |t| @panic(@tagName(t)), + }; + + instruction.value.llvm = value; + } + + _ = basic_block_command_buffer.popFirst(); } + if (debug_info) { const file_index = nat_function.declaration.file; const llvm_file = thread.debug_info_file_map.get_pointer(file_index).?; @@ -2879,6 +3086,131 @@ pub fn analyze_local_block(thread: *Thread, analyzer: *Analyzer, parser: *Parser local_block.scope.declarations.put_no_clobber(local_name, &local_symbol.local_declaration.declaration); }, + 'i' => { + if (src[parser.i + 1] == 'f') { + parser.i += 2; + + parser.skip_space(src); + + parser.expect_character(src, '('); + + parser.skip_space(src); + + const condition = parser.parse_expression(analyzer, thread, file, null, .right); + + parser.skip_space(src); + + parser.expect_character(src, ')'); + + parser.skip_space(src); + + const condition_type = condition.get_type(); + + const zero = thread.constant_ints.append(.{ + .value = .{ + .sema = .{ + .thread = thread.get_index(), + .resolved = true, + .id = .constant_int, + }, + }, + .n = 0, + .type = condition_type, + }); + + const compare = thread.integer_compares.append(.{ + .instruction = .{ + .value = .{ + .sema = .{ + .thread = thread.get_index(), + .resolved = true, + .id = .instruction, + }, + }, + .id = .integer_compare, + }, + .left = condition, + .right = &zero.value, + .id = .not_equal, + }); + _ = analyzer.current_basic_block.instructions.append(&compare.instruction); + + const taken_block = thread.basic_blocks.append(.{ + .value = .{ + .sema = .{ + .thread = thread.get_index(), + .resolved = true, + .id = .basic_block, + }, + }, + }); + const exit_block = thread.basic_blocks.append(.{ + .value = .{ + .sema = .{ + .thread = thread.get_index(), + .resolved = true, + .id = .basic_block, + }, + }, + }); + const original_block = analyzer.current_basic_block; + + const branch = thread.branches.append(.{ + .instruction = .{ + .value = .{ + .sema = .{ + .thread = thread.get_index(), + .resolved = true, + .id = .instruction, + }, + }, + .id = .branch, + }, + .condition = &compare.instruction.value, + .taken = taken_block, + .not_taken = exit_block, + }); + _ = analyzer.current_basic_block.instructions.append(&branch.instruction); + + analyzer.current_basic_block.is_terminated = true; + + analyzer.current_basic_block = branch.taken; + _ = branch.taken.predecessors.append(original_block); + + switch (src[parser.i]) { + '{' =>{ + analyze_local_block(thread, analyzer, parser, file); + }, + else => @panic((src.ptr + parser.i)[0..1]), + } + + parser.skip_space(src); + + if (src[parser.i] == 'e') { + if (byte_equal(src[parser.i..][0.."else".len], "else")) { + parser.i += "else".len; + _ = branch.taken.predecessors.append(original_block); + analyzer.current_basic_block = branch.not_taken; + + parser.skip_space(src); + + switch (src[parser.i]) { + '{' =>{ + analyze_local_block(thread, analyzer, parser, file); + }, + else => @panic((src.ptr + parser.i)[0..1]), + } + } else { + unreachable; + } + } else { + unreachable; + } + + } else { + unreachable; + } + }, else => { exit_with_error("Unrecognized statement initial char"); }, @@ -2929,8 +3261,15 @@ pub fn analyze_file(thread: *Thread, file_index: u32) void { parser.skip_space(src); const function = thread.functions.add_one(); - const entry_block = thread.basic_blocks.append(.{}); - const entry_block_index = thread.basic_blocks.get_typed_index(entry_block); + const entry_block = thread.basic_blocks.append(.{ + .value = .{ + .sema = .{ + .thread = thread.get_index(), + .resolved = true, + .id = .basic_block, + }, + }, + }); analyzer.current_function = function; analyzer.current_basic_block = entry_block; @@ -2962,7 +3301,7 @@ pub fn analyze_file(thread: *Thread, file_index: u32) void { .parent = &file.scope.scope, }, }, - .entry_block = entry_block_index, + .entry_block = entry_block, }; const has_function_attributes = src[parser.i] == '['; @@ -3937,6 +4276,7 @@ pub const LLVM = struct { const toConstant = bindings.NativityLLVMValueToConstant; const toFunction = bindings.NativityLLVMValueToFunction; const toAlloca = bindings.NativityLLVMValueToAlloca; + const toBasicBlock = bindings.NativityLLVMValueToBasicBlock; const toString = bindings.NativityLLVMValueToString; pub const IntrinsicID = enum(u32) { diff --git a/retest/standalone/if_else/main.nat b/retest/standalone/if_else/main.nat new file mode 100644 index 0000000..e3d9f2b --- /dev/null +++ b/retest/standalone/if_else/main.nat @@ -0,0 +1,11 @@ +fn foo() s32 { + return 1; +} +fn [cc(.c)] main [export] () s32 { + >a = foo(); + if (a == 1) { + return 0; + } else { + return 1; + } +} diff --git a/src/llvm/llvm.cpp b/src/llvm/llvm.cpp index 96155cf..1c6e001 100644 --- a/src/llvm/llvm.cpp +++ b/src/llvm/llvm.cpp @@ -794,6 +794,12 @@ extern "C" Function* NativityLLVMValueToFunction(Value* value) return function; } +extern "C" BasicBlock* NativityLLVMValueToBasicBlock(Value* value) +{ + auto* basic_block = dyn_cast(value); + return basic_block; +} + extern "C" bool NativityLLVMTypeIsPointer(Type* type) { bool is_pointer = type->isPointerTy();