From d4ec0279d3ec59cf33b1b2fbf74f9ae82af753cc Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Sat, 9 Jan 2021 16:22:43 +0100 Subject: stage2: add support for optionals in the LLVM backend We can now codegen optionals! This includes the following instructions: - is_null - is_null_ptr - is_non_null - is_non_null_ptr - optional_payload - optional_payload_ptr - br_void Also includes a test for optionals. --- src/codegen/llvm.zig | 113 +++++++++++++++++++++++++++++++++++++++--- src/codegen/llvm/bindings.zig | 9 ++++ 2 files changed, 114 insertions(+), 8 deletions(-) (limited to 'src/codegen') diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index df6a58b1e2..f087957f1d 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -397,6 +397,7 @@ pub const LLVMIRModule = struct { .block => try self.genBlock(inst.castTag(.block).?), .br => try self.genBr(inst.castTag(.br).?), .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?), + .br_void => try self.genBrVoid(inst.castTag(.br_void).?), .call => try self.genCall(inst.castTag(.call).?), .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?, .eq), .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?, .gt), @@ -406,6 +407,10 @@ pub const LLVMIRModule = struct { .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?, .neq), .condbr => try self.genCondBr(inst.castTag(.condbr).?), .intcast => try self.genIntCast(inst.castTag(.intcast).?), + .is_non_null => try self.genIsNonNull(inst.castTag(.is_non_null).?, false), + .is_non_null_ptr => try self.genIsNonNull(inst.castTag(.is_non_null_ptr).?, true), + .is_null => try self.genIsNull(inst.castTag(.is_null).?, false), + .is_null_ptr => try self.genIsNull(inst.castTag(.is_null_ptr).?, true), .load => try self.genLoad(inst.castTag(.load).?), .loop => try self.genLoop(inst.castTag(.loop).?), .not => try self.genNot(inst.castTag(.not).?), @@ -414,6 +419,8 @@ pub const LLVMIRModule = struct { .store => try self.genStore(inst.castTag(.store).?), .sub => try self.genSub(inst.castTag(.sub).?), .unreach => self.genUnreach(inst.castTag(.unreach).?), + .optional_payload => try self.genOptionalPayload(inst.castTag(.optional_payload).?, false), + .optional_payload_ptr => try self.genOptionalPayload(inst.castTag(.optional_payload_ptr).?, true), .dbg_stmt => blk: { // TODO: implement debug info break :blk null; @@ -534,21 +541,29 @@ pub const LLVMIRModule = struct { } fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value { - // Get the block that we want to break to. var block = self.blocks.get(inst.block).?; - _ = self.builder.buildBr(block.parent_bb); // If the break doesn't break a value, then we don't have to add // the values to the lists. - if (!inst.operand.ty.hasCodeGenBits()) return null; + if (!inst.operand.ty.hasCodeGenBits()) { + // TODO: in astgen these instructions should turn into `br_void` instructions. + _ = self.builder.buildBr(block.parent_bb); + } else { + const val = try self.resolveInst(inst.operand); - // For the phi node, we need the basic blocks and the values of the - // break instructions. - try block.break_bbs.append(self.gpa, self.builder.getInsertBlock()); + // For the phi node, we need the basic blocks and the values of the + // break instructions. + try block.break_bbs.append(self.gpa, self.builder.getInsertBlock()); + try block.break_vals.append(self.gpa, val); - const val = try self.resolveInst(inst.operand); - try block.break_vals.append(self.gpa, val); + _ = self.builder.buildBr(block.parent_bb); + } + return null; + } + fn genBrVoid(self: *LLVMIRModule, inst: *Inst.BrVoid) !?*const llvm.Value { + var block = self.blocks.get(inst.block).?; + _ = self.builder.buildBr(block.parent_bb); return null; } @@ -591,6 +606,44 @@ pub const LLVMIRModule = struct { return null; } + fn genIsNonNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { + const operand = try self.resolveInst(inst.operand); + + if (operand_is_ptr) { + const index_type = self.context.intType(32); + + var indices: [2]*const llvm.Value = .{ + index_type.constNull(), + index_type.constInt(1, false), + }; + + return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, 2, ""), ""); + } else { + return self.builder.buildExtractValue(operand, 1, ""); + } + } + + fn genIsNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { + return self.builder.buildNot((try self.genIsNonNull(inst, operand_is_ptr)).?, ""); + } + + fn genOptionalPayload(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value { + const operand = try self.resolveInst(inst.operand); + + if (operand_is_ptr) { + const index_type = self.context.intType(32); + + var indices: [2]*const llvm.Value = .{ + index_type.constNull(), + index_type.constNull(), + }; + + return self.builder.buildInBoundsGEP(operand, &indices, 2, ""); + } else { + return self.builder.buildExtractValue(operand, 0, ""); + } + } + fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value { const lhs = try self.resolveInst(inst.lhs); const rhs = try self.resolveInst(inst.rhs); @@ -751,6 +804,13 @@ pub const LLVMIRModule = struct { // TODO: consider using buildInBoundsGEP2 for opaque pointers return self.builder.buildInBoundsGEP(val, &indices, 2, ""); }, + .ref_val => { + const elem_value = tv.val.castTag(.ref_val).?.data; + const elem_type = tv.ty.castPointer().?.data; + const alloca = self.buildAlloca(try self.getLLVMType(elem_type, src)); + _ = self.builder.buildStore(try self.genTypedValue(src, .{ .ty = elem_type, .val = elem_value }), alloca); + return alloca; + }, else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}), }, .Array => { @@ -765,6 +825,29 @@ pub const LLVMIRModule = struct { return self.fail(src, "TODO handle more array values", .{}); } }, + .Optional => { + if (!tv.ty.isPtrLikeOptional()) { + var buf: Type.Payload.ElemType = undefined; + const child_type = tv.ty.optionalChild(&buf); + const llvm_child_type = try self.getLLVMType(child_type, src); + + if (tv.val.tag() == .null_value) { + var optional_values: [2]*const llvm.Value = .{ + llvm_child_type.constNull(), + self.context.intType(1).constNull(), + }; + return self.context.constStruct(&optional_values, 2, false); + } else { + var optional_values: [2]*const llvm.Value = .{ + try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }), + self.context.intType(1).constAllOnes(), + }; + return self.context.constStruct(&optional_values, 2, false); + } + } else { + return self.fail(src, "TODO implement const of optional pointer", .{}); + } + }, else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}), } } @@ -790,6 +873,20 @@ pub const LLVMIRModule = struct { const elem_type = try self.getLLVMType(t.elemType(), src); return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget()))); }, + .Optional => { + if (!t.isPtrLikeOptional()) { + var buf: Type.Payload.ElemType = undefined; + const child_type = t.optionalChild(&buf); + + var optional_types: [2]*const llvm.Type = .{ + try self.getLLVMType(child_type, src), + self.context.intType(1), + }; + return self.context.structType(&optional_types, 2, false); + } else { + return self.fail(src, "TODO implement optional pointers as actual pointers", .{}); + } + }, else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}), } } diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 6474957b1c..ccba3d9973 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -21,9 +21,15 @@ pub const Context = opaque { pub const voidType = LLVMVoidTypeInContext; extern fn LLVMVoidTypeInContext(C: *const Context) *const Type; + pub const structType = LLVMStructTypeInContext; + extern fn LLVMStructTypeInContext(C: *const Context, ElementTypes: [*]*const Type, ElementCount: c_uint, Packed: LLVMBool) *const Type; + pub const constString = LLVMConstStringInContext; extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value; + pub const constStruct = LLVMConstStructInContext; + extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: LLVMBool) *const Value; + pub const createBasicBlock = LLVMCreateBasicBlockInContext; extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock; @@ -204,6 +210,9 @@ pub const Builder = opaque { pub const buildPhi = LLVMBuildPhi; extern fn LLVMBuildPhi(*const Builder, Ty: *const Type, Name: [*:0]const u8) *const Value; + + pub const buildExtractValue = LLVMBuildExtractValue; + extern fn LLVMBuildExtractValue(*const Builder, AggVal: *const Value, Index: c_uint, Name: [*:0]const u8) *const Value; }; pub const IntPredicate = extern enum { -- cgit v1.2.3