diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/AstGen.zig | 9 | ||||
| -rw-r--r-- | src/Module.zig | 4 | ||||
| -rw-r--r-- | src/Sema.zig | 285 | ||||
| -rw-r--r-- | src/arch/arm/CodeGen.zig | 34 | ||||
| -rw-r--r-- | src/arch/arm/Mir.zig | 12 | ||||
| -rw-r--r-- | src/arch/arm/bits.zig | 77 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 2 | ||||
| -rw-r--r-- | src/type.zig | 2 |
8 files changed, 336 insertions, 89 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig index b9ac0b5c43..e8d612fbc7 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -244,10 +244,14 @@ pub const ResultLoc = union(enum) { fn strategy(rl: ResultLoc, block_scope: *GenZir) Strategy { switch (rl) { // In this branch there will not be any store_to_block_ptr instructions. - .discard, .none, .ty, .coerced_ty, .ref => return .{ + .none, .ty, .coerced_ty, .ref => return .{ .tag = .break_operand, .elide_store_to_block_ptr_instructions = false, }, + .discard => return .{ + .tag = .break_void, + .elide_store_to_block_ptr_instructions = false, + }, // The pointer got passed through to the sub-expressions, so we will use // break_void here. // In this branch there will not be any store_to_block_ptr instructions. @@ -1766,6 +1770,9 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn // we assume the result location is written, and we break with void. _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); }, + .discard => { + _ = try parent_gz.addBreak(break_tag, block_inst, .void_value); + }, else => { _ = try parent_gz.addBreak(break_tag, block_inst, operand); }, diff --git a/src/Module.zig b/src/Module.zig index 048895e5bf..8fed3138e7 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1125,6 +1125,8 @@ pub const Union = struct { abi_align: Value, /// Returns the field alignment, assuming the union is not packed. + /// Keep implementation in sync with `Sema.unionFieldAlignment`. + /// Prefer to call that function instead of this one during Sema. pub fn normalAlignment(field: Field, target: Target) u32 { if (field.abi_align.tag() == .abi_align_default) { return field.ty.abiAlignment(target); @@ -3762,7 +3764,7 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { // Note this resolves the type of the Decl, not the value; if this Decl // is a struct, for example, this resolves `type` (which needs no resolution), // not the struct itself. - try sema.resolveTypeLayout(&block_scope, src, decl_tv.ty); + try sema.resolveTypeFully(&block_scope, src, decl_tv.ty); const decl_arena_state = try decl_arena_allocator.create(std.heap.ArenaAllocator.State); diff --git a/src/Sema.zig b/src/Sema.zig index 8e93d2525b..1fdc9e02eb 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -4494,6 +4494,10 @@ fn analyzeCall( try sema.emitBackwardBranch(&child_block, call_src); + // Whether this call should be memoized, set to false if the call can mutate + // comptime state. + var should_memoize = true; + // This will have return instructions analyzed as break instructions to // the block_inst above. Here we are performing "comptime/inline semantic analysis" // for a function body, which means we must map the parameter ZIR instructions to @@ -4527,6 +4531,7 @@ fn analyzeCall( }, else => {}, } + should_memoize = should_memoize and !arg_val.isComptimeMutablePtr(); memoized_call_key.args[arg_i] = .{ .ty = param_ty, .val = arg_val, @@ -4552,6 +4557,7 @@ fn analyzeCall( }, else => {}, } + should_memoize = should_memoize and !arg_val.isComptimeMutablePtr(); memoized_call_key.args[arg_i] = .{ .ty = sema.typeOf(uncasted_arg), .val = arg_val, @@ -4597,7 +4603,7 @@ fn analyzeCall( // This `res2` is here instead of directly breaking from `res` due to a stage1 // bug generating invalid LLVM IR. const res2: Air.Inst.Ref = res2: { - if (is_comptime_call) { + if (should_memoize and is_comptime_call) { if (mod.memoized_calls.get(memoized_call_key)) |result| { const ty_inst = try sema.addType(fn_ret_ty); try sema.air_values.append(gpa, result.val); @@ -4621,7 +4627,7 @@ fn analyzeCall( break :result try sema.analyzeBlockBody(block, call_src, &child_block, merges); }; - if (is_comptime_call) { + if (should_memoize and is_comptime_call) { const result_val = try sema.resolveConstMaybeUndefVal(block, call_src, result); // TODO: check whether any external comptime memory was mutated by the @@ -9897,63 +9903,63 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai type_info_ty, try Value.Tag.@"union".create(sema.arena, .{ .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Type)), - .val = Value.initTag(.unreachable_value), + .val = Value.@"void", }), ), .Void => return sema.addConstant( type_info_ty, try Value.Tag.@"union".create(sema.arena, .{ .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Void)), - .val = Value.initTag(.unreachable_value), + .val = Value.@"void", }), ), .Bool => return sema.addConstant( type_info_ty, try Value.Tag.@"union".create(sema.arena, .{ .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Bool)), - .val = Value.initTag(.unreachable_value), + .val = Value.@"void", }), ), .NoReturn => return sema.addConstant( type_info_ty, try Value.Tag.@"union".create(sema.arena, .{ .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.NoReturn)), - .val = Value.initTag(.unreachable_value), + .val = Value.@"void", }), ), .ComptimeFloat => return sema.addConstant( type_info_ty, try Value.Tag.@"union".create(sema.arena, .{ .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ComptimeFloat)), - .val = Value.initTag(.unreachable_value), + .val = Value.@"void", }), ), .ComptimeInt => return sema.addConstant( type_info_ty, try Value.Tag.@"union".create(sema.arena, .{ .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.ComptimeInt)), - .val = Value.initTag(.unreachable_value), + .val = Value.@"void", }), ), .Undefined => return sema.addConstant( type_info_ty, try Value.Tag.@"union".create(sema.arena, .{ .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Undefined)), - .val = Value.initTag(.unreachable_value), + .val = Value.@"void", }), ), .Null => return sema.addConstant( type_info_ty, try Value.Tag.@"union".create(sema.arena, .{ .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.Null)), - .val = Value.initTag(.unreachable_value), + .val = Value.@"void", }), ), .EnumLiteral => return sema.addConstant( type_info_ty, try Value.Tag.@"union".create(sema.arena, .{ .tag = try Value.Tag.enum_field_index.create(sema.arena, @enumToInt(std.builtin.TypeId.EnumLiteral)), - .val = Value.initTag(.unreachable_value), + .val = Value.@"void", }), ), .Fn => { @@ -10374,6 +10380,9 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }; const union_ty = try sema.resolveTypeFields(block, src, ty); + try sema.resolveTypeLayout(block, src, ty); // Getting alignment requires type layout + const layout = union_ty.containerLayout(); + const union_fields = union_ty.unionFields(); const union_field_vals = try fields_anon_decl.arena().alloc(Value, union_fields.count()); @@ -10392,13 +10401,18 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }; const union_field_fields = try fields_anon_decl.arena().create([3]Value); + const alignment = switch (layout) { + .Auto, .Extern => try sema.unionFieldAlignment(block, src, field), + .Packed => 0, + }; + union_field_fields.* = .{ // name: []const u8, name_val, // field_type: type, try Value.Tag.ty.create(fields_anon_decl.arena(), field.ty), // alignment: comptime_int, - try field.abi_align.copy(fields_anon_decl.arena()), + try Value.Tag.int_u64.create(fields_anon_decl.arena(), alignment), }; field_val.* = try Value.Tag.@"struct".create(fields_anon_decl.arena(), union_field_fields); } @@ -10429,7 +10443,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai // layout: ContainerLayout, try Value.Tag.enum_field_index.create( sema.arena, - @enumToInt(union_ty.containerLayout()), + @enumToInt(layout), ), // tag_type: ?type, @@ -10467,6 +10481,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai break :t try struct_field_ty_decl.val.toType(&buffer).copy(fields_anon_decl.arena()); }; const struct_ty = try sema.resolveTypeFields(block, src, ty); + try sema.resolveTypeLayout(block, src, ty); // Getting alignment requires type layout const layout = struct_ty.containerLayout(); const struct_field_vals = fv: { @@ -10975,8 +10990,7 @@ fn zirCondbr( if (try sema.resolveDefinedValue(parent_block, src, cond)) |cond_val| { const body = if (cond_val.toBool()) then_body else else_body; - _ = try sema.analyzeBody(parent_block, body); - return always_noreturn; + return sema.analyzeBodyInner(parent_block, body); } const gpa = sema.gpa; @@ -11837,6 +11851,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const operand = sema.resolveInst(inst_data.operand); const operand_ty = sema.typeOf(operand); + try sema.resolveTypeLayout(block, operand_src, operand_ty); const enum_ty = switch (operand_ty.zigTypeTag()) { .EnumLiteral => { const val = try sema.resolveConstValue(block, operand_src, operand); @@ -11932,7 +11947,23 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I const ty = try Type.vector(sema.arena, len, try child_ty.copy(sema.arena)); return sema.addType(ty); }, - .Float => return sema.fail(block, src, "TODO: Sema.zirReify for Float", .{}), + .Float => { + const struct_val = union_val.val.castTag(.@"struct").?.data; + // TODO use reflection instead of magic numbers here + // bits: comptime_int, + const bits_val = struct_val[0]; + + const bits = @intCast(u16, bits_val.toUnsignedInt()); + const ty = switch (bits) { + 16 => Type.@"f16", + 32 => Type.@"f32", + 64 => Type.@"f64", + 80 => Type.@"f80", + 128 => Type.@"f128", + else => return sema.fail(block, src, "{}-bit float unsupported", .{bits}), + }; + return sema.addType(ty); + }, .Pointer => { const struct_val = union_val.val.castTag(.@"struct").?.data; // TODO use reflection instead of magic numbers here @@ -11948,26 +11979,82 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I var buffer: Value.ToTypeBuffer = undefined; const child_ty = child_val.toType(&buffer); + const ptr_size = size_val.toEnum(std.builtin.TypeInfo.Pointer.Size); + + var actual_sentinel: ?Value = null; if (!sentinel_val.isNull()) { - return sema.fail(block, src, "TODO: implement zirReify for pointer with non-null sentinel", .{}); + if (ptr_size == .One or ptr_size == .C) { + return sema.fail(block, src, "sentinels are only allowed on slices and unknown-length pointers", .{}); + } + const sentinel_ptr_val = sentinel_val.castTag(.opt_payload).?.data; + const ptr_ty = try Type.ptr(sema.arena, .{ .@"addrspace" = .generic, .pointee_type = child_ty }); + actual_sentinel = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?; } const ty = try Type.ptr(sema.arena, .{ - .size = size_val.toEnum(std.builtin.TypeInfo.Pointer.Size), + .size = ptr_size, .mutable = !is_const_val.toBool(), .@"volatile" = is_volatile_val.toBool(), .@"align" = @intCast(u8, alignment_val.toUnsignedInt()), // TODO: Validate this value. .@"addrspace" = address_space_val.toEnum(std.builtin.AddressSpace), .pointee_type = try child_ty.copy(sema.arena), .@"allowzero" = is_allowzero_val.toBool(), - .sentinel = null, + .sentinel = actual_sentinel, }); return sema.addType(ty); }, - .Array => return sema.fail(block, src, "TODO: Sema.zirReify for Array", .{}), + .Array => { + const struct_val = union_val.val.castTag(.@"struct").?.data; + // TODO use reflection instead of magic numbers here + // len: comptime_int, + const len_val = struct_val[0]; + // child: type, + const child_val = struct_val[1]; + // sentinel: ?*const anyopaque, + const sentinel_val = struct_val[2]; + + const len = len_val.toUnsignedInt(); + var buffer: Value.ToTypeBuffer = undefined; + const child_ty = try child_val.toType(&buffer).copy(sema.arena); + const sentinel = if (sentinel_val.castTag(.opt_payload)) |p| blk: { + const ptr_ty = try Type.ptr(sema.arena, .{ .@"addrspace" = .generic, .pointee_type = child_ty }); + break :blk (try sema.pointerDeref(block, src, p.data, ptr_ty)).?; + } else null; + + const ty = try Type.array(sema.arena, len, sentinel, child_ty); + return sema.addType(ty); + }, .Struct => return sema.fail(block, src, "TODO: Sema.zirReify for Struct", .{}), - .Optional => return sema.fail(block, src, "TODO: Sema.zirReify for Optional", .{}), - .ErrorUnion => return sema.fail(block, src, "TODO: Sema.zirReify for ErrorUnion", .{}), + .Optional => { + const struct_val = union_val.val.castTag(.@"struct").?.data; + // TODO use reflection instead of magic numbers here + // child: type, + const child_val = struct_val[0]; + + var buffer: Value.ToTypeBuffer = undefined; + const child_ty = try child_val.toType(&buffer).copy(sema.arena); + + const ty = try Type.optional(sema.arena, child_ty); + return sema.addType(ty); + }, + .ErrorUnion => { + const struct_val = union_val.val.castTag(.@"struct").?.data; + // TODO use reflection instead of magic numbers here + // error_set: type, + const error_set_val = struct_val[0]; + // payload: type, + const payload_val = struct_val[1]; + + var buffer: Value.ToTypeBuffer = undefined; + const error_set_ty = try error_set_val.toType(&buffer).copy(sema.arena); + const payload_ty = try payload_val.toType(&buffer).copy(sema.arena); + + const ty = try Type.Tag.error_union.create(sema.arena, .{ + .error_set = error_set_ty, + .payload = payload_ty, + }); + return sema.addType(ty); + }, .ErrorSet => return sema.fail(block, src, "TODO: Sema.zirReify for ErrorSet", .{}), .Enum => return sema.fail(block, src, "TODO: Sema.zirReify for Enum", .{}), .Union => return sema.fail(block, src, "TODO: Sema.zirReify for Union", .{}), @@ -12070,6 +12157,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const type_res = try sema.resolveType(block, src, extra.lhs); try sema.checkPtrType(block, type_src, type_res); + _ = try sema.resolveTypeLayout(block, src, type_res.childType()); const ptr_align = type_res.ptrAlignment(sema.mod.getTarget()); if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| { @@ -13066,9 +13154,59 @@ fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. } fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const tracy = trace(@src()); + defer tracy.end(); + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; - const src = inst_data.src(); - return sema.fail(block, src, "TODO: Sema.zirBuiltinCall", .{}); + const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; + const func_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; + const args_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; + const call_src = inst_data.src(); + + const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index); + var func = sema.resolveInst(extra.data.callee); + const options = sema.resolveInst(extra.data.options); + const args = sema.resolveInst(extra.data.args); + + const modifier: std.builtin.CallOptions.Modifier = modifier: { + const export_options_ty = try sema.getBuiltinType(block, options_src, "CallOptions"); + const coerced_options = try sema.coerce(block, export_options_ty, options, options_src); + const options_val = try sema.resolveConstValue(block, options_src, coerced_options); + const fields = options_val.castTag(.@"struct").?.data; + const struct_obj = export_options_ty.castTag(.@"struct").?.data; + const modifier_index = struct_obj.fields.getIndex("modifier").?; + const stack_index = struct_obj.fields.getIndex("stack").?; + if (!fields[stack_index].isNull()) { + return sema.fail(block, options_src, "TODO: implement @call with stack", .{}); + } + break :modifier fields[modifier_index].toEnum(std.builtin.CallOptions.Modifier); + }; + + const args_ty = sema.typeOf(args); + if (!args_ty.isTuple() and args_ty.tag() != .empty_struct_literal) { + return sema.fail(block, args_src, "expected a tuple, found {}", .{args_ty}); + } + + var resolved_args: []Air.Inst.Ref = undefined; + + // Desugar bound functions here + if (sema.typeOf(func).tag() == .bound_fn) { + const bound_func = try sema.resolveValue(block, func_src, func); + const bound_data = &bound_func.cast(Value.Payload.BoundFn).?.data; + func = bound_data.func_inst; + resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount() + 1); + resolved_args[0] = bound_data.arg0_inst; + for (resolved_args[1..]) |*resolved, i| { + resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(u32, i), args_ty); + } + } else { + resolved_args = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount()); + for (resolved_args) |*resolved, i| { + resolved.* = try sema.tupleFieldValByIndex(block, args_src, args, @intCast(u32, i), args_ty); + } + } + + return sema.analyzeCall(block, func, func_src, call_src, modifier, false, resolved_args); } fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -14596,10 +14734,8 @@ fn tupleFieldVal( field_name_src: LazySrcLoc, tuple_ty: Type, ) CompileError!Air.Inst.Ref { - const tuple = tuple_ty.castTag(.tuple).?.data; - if (mem.eql(u8, field_name, "len")) { - return sema.addIntUnsigned(Type.usize, tuple.types.len); + return sema.addIntUnsigned(Type.usize, tuple_ty.structFieldCount()); } const field_index = std.fmt.parseUnsigned(u32, field_name, 10) catch |err| { @@ -14607,7 +14743,18 @@ fn tupleFieldVal( tuple_ty, field_name, @errorName(err), }); }; + return tupleFieldValByIndex(sema, block, src, tuple_byval, field_index, tuple_ty); +} +fn tupleFieldValByIndex( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + tuple_byval: Air.Inst.Ref, + field_index: u32, + tuple_ty: Type, +) CompileError!Air.Inst.Ref { + const tuple = tuple_ty.castTag(.tuple).?.data; const field_ty = tuple.types[field_index]; if (tuple.values[field_index].tag() != .unreachable_value) { @@ -17035,30 +17182,41 @@ fn cmpNumeric( if (try sema.resolveMaybeUndefVal(block, lhs_src, lhs)) |lhs_val| { if (lhs_val.isUndef()) return sema.addConstUndef(Type.bool); - const is_unsigned = if (lhs_is_float) x: { + if (!rhs_is_signed) { + switch (lhs_val.orderAgainstZero()) { + .gt => {}, + .eq => switch (op) { // LHS = 0, RHS is unsigned + .lte => return Air.Inst.Ref.bool_true, + .gt => return Air.Inst.Ref.bool_false, + else => {}, + }, + .lt => switch (op) { // LHS < 0, RHS is unsigned + .neq, .lt, .lte => return Air.Inst.Ref.bool_true, + .eq, .gt, .gte => return Air.Inst.Ref.bool_false, + }, + } + } + if (lhs_is_float) { var bigint_space: Value.BigIntSpace = undefined; var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(sema.gpa); defer bigint.deinit(); - const zcmp = lhs_val.orderAgainstZero(); if (lhs_val.floatHasFraction()) { switch (op) { .eq => return Air.Inst.Ref.bool_false, .neq => return Air.Inst.Ref.bool_true, else => {}, } - if (zcmp == .lt) { + if (lhs_is_signed) { try bigint.addScalar(bigint.toConst(), -1); } else { try bigint.addScalar(bigint.toConst(), 1); } } lhs_bits = bigint.toConst().bitCountTwosComp(); - break :x (zcmp != .lt); - } else x: { + } else { lhs_bits = lhs_val.intBitCountTwosComp(target); - break :x (lhs_val.orderAgainstZero() != .lt); - }; - lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed); + } + lhs_bits += @boolToInt(!lhs_is_signed and dest_int_is_signed); } else if (lhs_is_float) { dest_float_type = lhs_ty; } else { @@ -17070,30 +17228,41 @@ fn cmpNumeric( if (try sema.resolveMaybeUndefVal(block, rhs_src, rhs)) |rhs_val| { if (rhs_val.isUndef()) return sema.addConstUndef(Type.bool); - const is_unsigned = if (rhs_is_float) x: { + if (!lhs_is_signed) { + switch (rhs_val.orderAgainstZero()) { + .gt => {}, + .eq => switch (op) { // RHS = 0, LHS is unsigned + .gte => return Air.Inst.Ref.bool_true, + .lt => return Air.Inst.Ref.bool_false, + else => {}, + }, + .lt => switch (op) { // RHS < 0, LHS is unsigned + .neq, .gt, .gte => return Air.Inst.Ref.bool_true, + .eq, .lt, .lte => return Air.Inst.Ref.bool_false, + }, + } + } + if (rhs_is_float) { var bigint_space: Value.BigIntSpace = undefined; var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(sema.gpa); defer bigint.deinit(); - const zcmp = rhs_val.orderAgainstZero(); if (rhs_val.floatHasFraction()) { switch (op) { .eq => return Air.Inst.Ref.bool_false, .neq => return Air.Inst.Ref.bool_true, else => {}, } - if (zcmp == .lt) { + if (rhs_is_signed) { try bigint.addScalar(bigint.toConst(), -1); } else { try bigint.addScalar(bigint.toConst(), 1); } } rhs_bits = bigint.toConst().bitCountTwosComp(); - break :x (zcmp != .lt); - } else x: { + } else { rhs_bits = rhs_val.intBitCountTwosComp(target); - break :x (rhs_val.orderAgainstZero() != .lt); - }; - rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed); + } + rhs_bits += @boolToInt(!rhs_is_signed and dest_int_is_signed); } else if (rhs_is_float) { dest_float_type = rhs_ty; } else { @@ -17587,7 +17756,7 @@ fn resolvePeerTypes( return chosen_ty; } -pub fn resolveTypeLayout( +fn resolveTypeLayout( sema: *Sema, block: *Block, src: LazySrcLoc, @@ -17603,6 +17772,10 @@ pub fn resolveTypeLayout( .Optional => { var buf: Type.Payload.ElemType = undefined; const payload_ty = ty.optionalChild(&buf); + // In case of querying the ABI alignment of this optional, we will ask + // for hasRuntimeBits() of the payload type, so we need "requires comptime" + // to be known already before this function returns. + _ = try sema.typeRequiresComptime(block, src, payload_ty); return sema.resolveTypeLayout(block, src, payload_ty); }, .ErrorUnion => { @@ -17634,6 +17807,13 @@ fn resolveStructLayout( try sema.resolveTypeLayout(block, src, field.ty); } struct_obj.status = .have_layout; + + // In case of querying the ABI alignment of this struct, we will ask + // for hasRuntimeBits() of each field, so we need "requires comptime" + // to be known already before this function returns. + for (struct_obj.fields.values()) |field| { + _ = try sema.typeRequiresComptime(block, src, field.ty); + } } // otherwise it's a tuple; no need to resolve anything } @@ -17660,7 +17840,7 @@ fn resolveUnionLayout( union_obj.status = .have_layout; } -fn resolveTypeFully( +pub fn resolveTypeFully( sema: *Sema, block: *Block, src: LazySrcLoc, @@ -19187,6 +19367,21 @@ fn typeAbiAlignment(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !u32 return ty.abiAlignment(target); } +/// Not valid to call for packed unions. +/// Keep implementation in sync with `Module.Union.Field.normalAlignment`. +fn unionFieldAlignment( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + field: Module.Union.Field, +) !u32 { + if (field.abi_align.tag() == .abi_align_default) { + return sema.typeAbiAlignment(block, src, field.ty); + } else { + return @intCast(u32, field.abi_align.toUnsignedInt()); + } +} + /// Synchronize logic with `Type.isFnOrHasRuntimeBits`. pub fn fnHasRuntimeBits(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { const fn_info = ty.fnInfo(); diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index f4e97fa8b1..6cd4a1fd9f 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -433,7 +433,9 @@ fn gen(self: *Self) !void { }); // exitlude jumps - if (self.exitlude_jump_relocs.items.len == 1) { + const only_one_exitlude_jump = self.exitlude_jump_relocs.items.len == 1 and + self.exitlude_jump_relocs.items[0] == self.mir_instructions.len - 1; + if (only_one_exitlude_jump) { // There is only one relocation. Hence, // this relocation must be at the end of // the code. Therefore, we can just delete @@ -1066,7 +1068,17 @@ fn airMax(self: *Self, inst: Air.Inst.Index) !void { fn airSlice(self: *Self, inst: Air.Inst.Index) !void { const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice for {}", .{self.target.cpu.arch}); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const ptr = try self.resolveInst(bin_op.lhs); + const ptr_ty = self.air.typeOf(bin_op.lhs); + const len = try self.resolveInst(bin_op.rhs); + const len_ty = self.air.typeOf(bin_op.rhs); + + const stack_offset = try self.allocMem(inst, 8, 8); + try self.genSetStack(ptr_ty, stack_offset + 4, ptr); + try self.genSetStack(len_ty, stack_offset, len); + break :result MCValue{ .stack_offset = stack_offset }; + }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -1359,6 +1371,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { const base_mcv: MCValue = switch (slice_mcv) { .stack_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_offset = off + 4 }) }, + .stack_argument_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_argument_offset = off + 4 }) }, else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}), }; self.register_manager.freezeRegs(&.{base_mcv.register}); @@ -3854,9 +3867,17 @@ fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airArrayToSlice for {}", .{ - self.target.cpu.arch, - }); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const ptr_ty = self.air.typeOf(ty_op.operand); + const ptr = try self.resolveInst(ty_op.operand); + const array_ty = ptr_ty.childType(); + const array_len = @intCast(u32, array_ty.arrayLenIncludingSentinel()); + + const stack_offset = try self.allocMem(inst, 8, 8); + try self.genSetStack(ptr_ty, stack_offset + 4, ptr); + try self.genSetStack(Type.initTag(.usize), stack_offset, .{ .immediate = array_len }); + break :result MCValue{ .stack_offset = stack_offset }; + }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -4077,6 +4098,9 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { } switch (typed_value.ty.zigTypeTag()) { + .Array => { + return self.lowerUnnamedConst(typed_value); + }, .Pointer => switch (typed_value.ty.ptrSize()) { .Slice => { return self.lowerUnnamedConst(typed_value); diff --git a/src/arch/arm/Mir.zig b/src/arch/arm/Mir.zig index 7852a39885..5dcc3f7095 100644 --- a/src/arch/arm/Mir.zig +++ b/src/arch/arm/Mir.zig @@ -118,8 +118,6 @@ pub const Inst = struct { /// All instructions have a 8-byte payload, which is contained within /// this union. `Tag` determines which union field is active, as well as /// how to interpret the data within. - // TODO flatten down Data (remove use of tagged unions) to make it - // 8 bytes only pub const Data = union { /// No additional data /// @@ -231,11 +229,11 @@ pub const Inst = struct { // Make sure we don't accidentally make instructions bigger than expected. // Note that in Debug builds, Zig is allowed to insert a secret field for safety checks. - // comptime { - // if (builtin.mode != .Debug) { - // assert(@sizeOf(Data) == 8); - // } - // } + comptime { + if (builtin.mode != .Debug) { + assert(@sizeOf(Data) == 8); + } + } }; pub fn deinit(mir: *Mir, gpa: std.mem.Allocator) void { diff --git a/src/arch/arm/bits.zig b/src/arch/arm/bits.zig index 568b7580c8..a23f99f789 100644 --- a/src/arch/arm/bits.zig +++ b/src/arch/arm/bits.zig @@ -469,6 +469,23 @@ pub const Instruction = union(enum) { } }; + pub const AddressingMode = enum { + /// [<Rn>, <offset>] + /// + /// Address = Rn + offset + offset, + /// [<Rn>, <offset>]! + /// + /// Address = Rn + offset + /// Rn = Rn + offset + pre_index, + /// [<Rn>], <offset> + /// + /// Address = Rn + /// Rn = Rn + offset + post_index, + }; + /// Represents the offset operand of a load or store /// instruction. Data can be loaded from memory with either an /// immediate offset or an offset that is stored in some register. @@ -730,10 +747,9 @@ pub const Instruction = union(enum) { rd: Register, rn: Register, offset: Offset, - pre_index: bool, + mode: AddressingMode, positive: bool, byte_word: u1, - write_back: bool, load_store: u1, ) Instruction { return Instruction{ @@ -743,10 +759,16 @@ pub const Instruction = union(enum) { .rd = rd.id(), .offset = offset.toU12(), .load_store = load_store, - .write_back = @boolToInt(write_back), + .write_back = switch (mode) { + .offset => 0b0, + .pre_index, .post_index => 0b1, + }, .byte_word = byte_word, .up_down = @boolToInt(positive), - .pre_post = @boolToInt(pre_index), + .pre_post = switch (mode) { + .offset, .pre_index => 0b1, + .post_index => 0b0, + }, .imm = @boolToInt(offset != .immediate), }, }; @@ -754,9 +776,8 @@ pub const Instruction = union(enum) { fn extraLoadStore( cond: Condition, - pre_index: bool, + mode: AddressingMode, positive: bool, - write_back: bool, o1: u1, op2: u2, rn: Register, @@ -780,10 +801,16 @@ pub const Instruction = union(enum) { .rt = rt.id(), .rn = rn.id(), .o1 = o1, - .write_back = @boolToInt(write_back), + .write_back = switch (mode) { + .offset => 0b0, + .pre_index, .post_index => 0b1, + }, .imm = @boolToInt(offset == .immediate), .up_down = @boolToInt(positive), - .pre_index = @boolToInt(pre_index), + .pre_index = switch (mode) { + .offset, .pre_index => 0b1, + .post_index => 0b0, + }, .cond = @enumToInt(cond), }, }; @@ -1091,51 +1118,49 @@ pub const Instruction = union(enum) { // Single data transfer pub const OffsetArgs = struct { - pre_index: bool = true, + mode: AddressingMode = .offset, positive: bool = true, offset: Offset, - write_back: bool = false, }; pub fn ldr(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { - return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 1); + return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 0, 1); } pub fn ldrb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { - return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 1); + return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 1, 1); } pub fn str(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { - return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 0, args.write_back, 0); + return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 0, 0); } pub fn strb(cond: Condition, rd: Register, rn: Register, args: OffsetArgs) Instruction { - return singleDataTransfer(cond, rd, rn, args.offset, args.pre_index, args.positive, 1, args.write_back, 0); + return singleDataTransfer(cond, rd, rn, args.offset, args.mode, args.positive, 1, 0); } // Extra load/store pub const ExtraLoadStoreOffsetArgs = struct { - pre_index: bool = true, + mode: AddressingMode = .offset, positive: bool = true, offset: ExtraLoadStoreOffset, - write_back: bool = false, }; pub fn strh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { - return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b0, 0b01, rn, rt, args.offset); + return extraLoadStore(cond, args.mode, args.positive, 0b0, 0b01, rn, rt, args.offset); } pub fn ldrh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { - return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b1, 0b01, rn, rt, args.offset); + return extraLoadStore(cond, args.mode, args.positive, 0b1, 0b01, rn, rt, args.offset); } pub fn ldrsh(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { - return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b1, 0b11, rn, rt, args.offset); + return extraLoadStore(cond, args.mode, args.positive, 0b1, 0b11, rn, rt, args.offset); } pub fn ldrsb(cond: Condition, rt: Register, rn: Register, args: ExtraLoadStoreOffsetArgs) Instruction { - return extraLoadStore(cond, args.pre_index, args.positive, args.write_back, 0b1, 0b10, rn, rt, args.offset); + return extraLoadStore(cond, args.mode, args.positive, 0b1, 0b10, rn, rt, args.offset); } // Block data transfer @@ -1234,10 +1259,9 @@ pub const Instruction = union(enum) { } else if (args.len == 1) { const reg = args[0]; return ldr(cond, reg, .sp, .{ - .pre_index = false, + .mode = .post_index, .positive = true, .offset = Offset.imm(4), - .write_back = false, }); } else { var register_list: u16 = 0; @@ -1259,10 +1283,9 @@ pub const Instruction = union(enum) { } else if (args.len == 1) { const reg = args[0]; return str(cond, reg, .sp, .{ - .pre_index = true, + .mode = .pre_index, .positive = false, .offset = Offset.imm(4), - .write_back = true, }); } else { var register_list: u16 = 0; @@ -1447,10 +1470,9 @@ test "aliases" { .{ // pop { r6 } .actual = Instruction.pop(.al, .{.r6}), .expected = Instruction.ldr(.al, .r6, .sp, .{ - .pre_index = false, + .mode = .post_index, .positive = true, .offset = Instruction.Offset.imm(4), - .write_back = false, }), }, .{ // pop { r1, r5 } @@ -1460,10 +1482,9 @@ test "aliases" { .{ // push { r3 } .actual = Instruction.push(.al, .{.r3}), .expected = Instruction.str(.al, .r3, .sp, .{ - .pre_index = true, + .mode = .pre_index, .positive = false, .offset = Instruction.Offset.imm(4), - .write_back = true, }), }, .{ // push { r0, r2 } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index c0642a59de..e349f0186e 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3892,7 +3892,7 @@ pub const FuncGen = struct { return self.builder.buildBitCast(operand, llvm_dest_ty.pointerType(0), ""); } - if (operand_ty.zigTypeTag() == .Int and inst_ty.zigTypeTag() == .Pointer) { + if (operand_ty.zigTypeTag() == .Int and inst_ty.isPtrAtRuntime()) { return self.builder.buildIntToPtr(operand, llvm_dest_ty, ""); } diff --git a/src/type.zig b/src/type.zig index 68085500bc..a84a0f4520 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2602,7 +2602,7 @@ pub const Type = extern union { const payload = self.castTag(.pointer).?.data; return payload.@"allowzero"; }, - else => false, + else => return self.zigTypeTag() == .Optional, }; } |
