diff options
Diffstat (limited to 'src/codegen')
| -rw-r--r-- | src/codegen/c.zig | 2 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 87 |
2 files changed, 89 insertions, 0 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 280b7604bf..b746818142 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -1952,6 +1952,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO .reduce_optimized, .float_to_int_optimized, => return f.fail("TODO implement optimized float mode", .{}), + + .is_named_enum_value => return f.fail("TODO: C backend: implement is_named_enum_value", .{}), // zig fmt: on }; switch (result_value) { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 5ea7ffad67..9c3efa18cd 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -201,6 +201,8 @@ pub const Object = struct { /// * it works for functions not all globals. /// Therefore, this table keeps track of the mapping. decl_map: std.AutoHashMapUnmanaged(Module.Decl.Index, *const llvm.Value), + /// Serves the same purpose as `decl_map` but only used for the `is_named_enum_value` instruction. + named_enum_map: std.AutoHashMapUnmanaged(Module.Decl.Index, *const llvm.Value), /// Maps Zig types to LLVM types. The table memory itself is backed by the GPA of /// the compiler, but the Type/Value memory here is backed by `type_map_arena`. /// TODO we need to remove entries from this map in response to incremental compilation @@ -377,6 +379,7 @@ pub const Object = struct { .target_data = target_data, .target = options.target, .decl_map = .{}, + .named_enum_map = .{}, .type_map = .{}, .type_map_arena = std.heap.ArenaAllocator.init(gpa), .di_type_map = .{}, @@ -396,6 +399,7 @@ pub const Object = struct { self.llvm_module.dispose(); self.context.dispose(); self.decl_map.deinit(gpa); + self.named_enum_map.deinit(gpa); self.type_map.deinit(gpa); self.type_map_arena.deinit(); self.extern_collisions.deinit(gpa); @@ -4180,6 +4184,8 @@ pub const FuncGen = struct { .union_init => try self.airUnionInit(inst), .prefetch => try self.airPrefetch(inst), + .is_named_enum_value => try self.airIsNamedEnumValue(inst), + .reduce => try self.airReduce(inst, false), .reduce_optimized => try self.airReduce(inst, true), @@ -7882,6 +7888,87 @@ pub const FuncGen = struct { } } + fn airIsNamedEnumValue(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { + if (self.liveness.isUnused(inst)) return null; + + const un_op = self.air.instructions.items(.data)[inst].un_op; + const operand = try self.resolveInst(un_op); + const enum_ty = self.air.typeOf(un_op); + + const llvm_fn = try self.getIsNamedEnumValueFunction(enum_ty); + const params = [_]*const llvm.Value{operand}; + return self.builder.buildCall(llvm_fn, ¶ms, params.len, .Fast, .Auto, ""); + } + + fn getIsNamedEnumValueFunction(self: *FuncGen, enum_ty: Type) !*const llvm.Value { + const enum_decl = enum_ty.getOwnerDecl(); + + // TODO: detect when the type changes and re-emit this function. + const gop = try self.dg.object.named_enum_map.getOrPut(self.dg.gpa, enum_decl); + if (gop.found_existing) return gop.value_ptr.*; + errdefer assert(self.dg.object.named_enum_map.remove(enum_decl)); + + var arena_allocator = std.heap.ArenaAllocator.init(self.gpa); + defer arena_allocator.deinit(); + const arena = arena_allocator.allocator(); + + const mod = self.dg.module; + const llvm_fn_name = try std.fmt.allocPrintZ(arena, "__zig_is_named_enum_value_{s}", .{ + try mod.declPtr(enum_decl).getFullyQualifiedName(mod), + }); + + var int_tag_type_buffer: Type.Payload.Bits = undefined; + const int_tag_ty = enum_ty.intTagType(&int_tag_type_buffer); + const param_types = [_]*const llvm.Type{try self.dg.lowerType(int_tag_ty)}; + + const llvm_ret_ty = try self.dg.lowerType(Type.bool); + const fn_type = llvm.functionType(llvm_ret_ty, ¶m_types, param_types.len, .False); + const fn_val = self.dg.object.llvm_module.addFunction(llvm_fn_name, fn_type); + fn_val.setLinkage(.Internal); + fn_val.setFunctionCallConv(.Fast); + self.dg.addCommonFnAttributes(fn_val); + gop.value_ptr.* = fn_val; + + const prev_block = self.builder.getInsertBlock(); + const prev_debug_location = self.builder.getCurrentDebugLocation2(); + defer { + self.builder.positionBuilderAtEnd(prev_block); + if (self.di_scope != null) { + self.builder.setCurrentDebugLocation2(prev_debug_location); + } + } + + const entry_block = self.dg.context.appendBasicBlock(fn_val, "Entry"); + self.builder.positionBuilderAtEnd(entry_block); + self.builder.clearCurrentDebugLocation(); + + const fields = enum_ty.enumFields(); + const named_block = self.dg.context.appendBasicBlock(fn_val, "Named"); + const unnamed_block = self.dg.context.appendBasicBlock(fn_val, "Unnamed"); + const tag_int_value = fn_val.getParam(0); + const switch_instr = self.builder.buildSwitch(tag_int_value, unnamed_block, @intCast(c_uint, fields.count())); + + for (fields.keys()) |_, field_index| { + const this_tag_int_value = int: { + var tag_val_payload: Value.Payload.U32 = .{ + .base = .{ .tag = .enum_field_index }, + .data = @intCast(u32, field_index), + }; + break :int try self.dg.lowerValue(.{ + .ty = enum_ty, + .val = Value.initPayload(&tag_val_payload.base), + }); + }; + switch_instr.addCase(this_tag_int_value, named_block); + } + self.builder.positionBuilderAtEnd(named_block); + _ = self.builder.buildRet(self.dg.context.intType(1).constInt(1, .False)); + + self.builder.positionBuilderAtEnd(unnamed_block); + _ = self.builder.buildRet(self.dg.context.intType(1).constInt(0, .False)); + return fn_val; + } + fn airTagName(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { if (self.liveness.isUnused(inst)) return null; |
