authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-28 19:39:54-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-06-28 19:45:10-04:00
logaa92446365b97e167c4162122c66a02b4cbca031
tree1844fc64f41303478182e86cb7dca5aad956dab0
parente120b07a524f1accd4975f5206018c61c4be9345

stage2: implement function parameters

In codegen.zig, the std.Target.Cpu.Arch is now generally available as a comptime value where needed. This is a tradeoff that causes the compiler binary to be more bloated, but gives us higher performance, since the optimizer can optimize per architecture (which is usually how compilers are designed anyway, with different code per-architecture), and it also allows us to use per-architecture types, such as a Register enum that is specific to the comptime-known architecture. Adds abiSize method to Type.

3 files changed, 286 insertions(+), 111 deletions(-)

src-self-hosted/codegen.zig+190-107
......@@ -37,6 +37,69 @@ pub fn generateSymbol(
3737 .Fn => {
3838 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
3939
40 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
41 const param_types = try bin_file.allocator.alloc(Type, fn_type.fnParamLen());
42 defer bin_file.allocator.free(param_types);
43 fn_type.fnParamTypes(param_types);
44 // A parameter may be broken into multiple machine code parameters, so we don't
45 // know the size up front.
46 var mc_args = try std.ArrayList(Function.MCValue).initCapacity(bin_file.allocator, param_types.len);
47 defer mc_args.deinit();
48
49 var next_stack_offset: u64 = 0;
50
51 switch (fn_type.fnCallingConvention()) {
52 .Naked => assert(mc_args.items.len == 0),
53 .Unspecified, .C => {
54 // Prepare the function parameters
55 switch (bin_file.options.target.cpu.arch) {
56 .x86_64 => {
57 const integer_registers = [_]Reg(.x86_64){.rdi, .rsi, .rdx, .rcx, .r8, .r9};
58 var next_int_reg: usize = 0;
59
60 for (param_types) |param_type, src_i| {
61 switch (param_type.zigTypeTag()) {
62 .Bool, .Int => {
63 if (next_int_reg >= integer_registers.len) {
64 try mc_args.append(.{ .stack_offset = next_stack_offset });
65 next_stack_offset += param_type.abiSize(bin_file.options.target);
66 } else {
67 try mc_args.append(.{ .register = @enumToInt(integer_registers[next_int_reg])});
68 next_int_reg += 1;
69 }
70 },
71 else => return Result{
72 .fail = try ErrorMsg.create(
73 bin_file.allocator,
74 src,
75 "TODO implement function parameters of type {}",
76 .{@tagName(param_type.zigTypeTag())},
77 ),
78 }
79 }
80 }
81
82 },
83 else => return Result{
84 .fail = try ErrorMsg.create(
85 bin_file.allocator,
86 src,
87 "TODO implement function parameters for {}",
88 .{bin_file.options.target.cpu.arch},
89 ),
90 },
91 }
92 },
93 else => return Result{
94 .fail = try ErrorMsg.create(
95 bin_file.allocator,
96 src,
97 "TODO implement {} calling convention",
98 .{fn_type.fnCallingConvention()},
99 ),
100 },
101 }
102
40103 var function = Function{
41104 .target = &bin_file.options.target,
42105 .bin_file = bin_file,
......@@ -44,16 +107,14 @@ pub fn generateSymbol(
44107 .code = code,
45108 .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(bin_file.allocator),
46109 .err_msg = null,
110 .args = mc_args.items,
47111 };
48112 defer function.inst_table.deinit();
49113
50 for (module_fn.analysis.success.instructions) |inst| {
51 const new_inst = function.genFuncInst(inst) catch |err| switch (err) {
52 error.CodegenFail => return Result{ .fail = function.err_msg.? },
53 else => |e| return e,
54 };
55 try function.inst_table.putNoClobber(inst, new_inst);
56 }
114 function.gen() catch |err| switch (err) {
115 error.CodegenFail => return Result{ .fail = function.err_msg.? },
116 else => |e| return e,
117 };
57118
58119 if (function.err_msg) |em| {
59120 return Result{ .fail = em };
......@@ -157,6 +218,7 @@ const Function = struct {
157218 code: *std.ArrayList(u8),
158219 inst_table: std.AutoHashMap(*ir.Inst, MCValue),
159220 err_msg: ?*ErrorMsg,
221 args: []MCValue,
160222
161223 const MCValue = union(enum) {
162224 none,
......@@ -170,44 +232,119 @@ const Function = struct {
170232 register: usize,
171233 /// The value is in memory at a hard-coded address.
172234 memory: u64,
235 /// The value is one of the stack variables.
236 stack_offset: u64,
173237 };
174238
175 fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue {
239 fn gen(self: *Function) !void {
240 switch (self.target.cpu.arch) {
241 .arm => return self.genArch(.arm),
242 .armeb => return self.genArch(.armeb),
243 .aarch64 => return self.genArch(.aarch64),
244 .aarch64_be => return self.genArch(.aarch64_be),
245 .aarch64_32 => return self.genArch(.aarch64_32),
246 .arc => return self.genArch(.arc),
247 .avr => return self.genArch(.avr),
248 .bpfel => return self.genArch(.bpfel),
249 .bpfeb => return self.genArch(.bpfeb),
250 .hexagon => return self.genArch(.hexagon),
251 .mips => return self.genArch(.mips),
252 .mipsel => return self.genArch(.mipsel),
253 .mips64 => return self.genArch(.mips64),
254 .mips64el => return self.genArch(.mips64el),
255 .msp430 => return self.genArch(.msp430),
256 .powerpc => return self.genArch(.powerpc),
257 .powerpc64 => return self.genArch(.powerpc64),
258 .powerpc64le => return self.genArch(.powerpc64le),
259 .r600 => return self.genArch(.r600),
260 .amdgcn => return self.genArch(.amdgcn),
261 .riscv32 => return self.genArch(.riscv32),
262 .riscv64 => return self.genArch(.riscv64),
263 .sparc => return self.genArch(.sparc),
264 .sparcv9 => return self.genArch(.sparcv9),
265 .sparcel => return self.genArch(.sparcel),
266 .s390x => return self.genArch(.s390x),
267 .tce => return self.genArch(.tce),
268 .tcele => return self.genArch(.tcele),
269 .thumb => return self.genArch(.thumb),
270 .thumbeb => return self.genArch(.thumbeb),
271 .i386 => return self.genArch(.i386),
272 .x86_64 => return self.genArch(.x86_64),
273 .xcore => return self.genArch(.xcore),
274 .nvptx => return self.genArch(.nvptx),
275 .nvptx64 => return self.genArch(.nvptx64),
276 .le32 => return self.genArch(.le32),
277 .le64 => return self.genArch(.le64),
278 .amdil => return self.genArch(.amdil),
279 .amdil64 => return self.genArch(.amdil64),
280 .hsail => return self.genArch(.hsail),
281 .hsail64 => return self.genArch(.hsail64),
282 .spir => return self.genArch(.spir),
283 .spir64 => return self.genArch(.spir64),
284 .kalimba => return self.genArch(.kalimba),
285 .shave => return self.genArch(.shave),
286 .lanai => return self.genArch(.lanai),
287 .wasm32 => return self.genArch(.wasm32),
288 .wasm64 => return self.genArch(.wasm64),
289 .renderscript32 => return self.genArch(.renderscript32),
290 .renderscript64 => return self.genArch(.renderscript64),
291 .ve => return self.genArch(.ve),
292 }
293 }
294
295 fn genArch(self: *Function, comptime arch: std.Target.Cpu.Arch) !void {
296 for (self.mod_fn.analysis.success.instructions) |inst| {
297 const new_inst = try self.genFuncInst(inst, arch);
298 try self.inst_table.putNoClobber(inst, new_inst);
299 }
300 }
301
302 fn genFuncInst(self: *Function, inst: *ir.Inst, comptime arch: std.Target.Cpu.Arch) !MCValue {
176303 switch (inst.tag) {
177 .add => return self.genAdd(inst.cast(ir.Inst.Add).?),
178 .arg => return self.genArg(inst.src),
179 .block => return self.genBlock(inst.cast(ir.Inst.Block).?),
180 .breakpoint => return self.genBreakpoint(inst.src),
181 .call => return self.genCall(inst.cast(ir.Inst.Call).?),
304 .add => return self.genAdd(inst.cast(ir.Inst.Add).?, arch),
305 .arg => return self.genArg(inst.cast(ir.Inst.Arg).?),
306 .block => return self.genBlock(inst.cast(ir.Inst.Block).?, arch),
307 .breakpoint => return self.genBreakpoint(inst.src, arch),
308 .call => return self.genCall(inst.cast(ir.Inst.Call).?, arch),
182309 .unreach => return MCValue{ .unreach = {} },
183310 .constant => unreachable, // excluded from function bodies
184 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),
311 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?, arch),
185312 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
186313 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
187 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?),
188 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?),
189 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?),
190 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?),
191 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?),
192 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?),
314 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?, arch),
315 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?, arch),
316 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?, arch),
317 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?, arch),
318 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?, arch),
319 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?, arch),
193320 }
194321 }
195322
196 fn genAdd(self: *Function, inst: *ir.Inst.Add) !MCValue {
197 switch (self.target.cpu.arch) {
323 fn genAdd(self: *Function, inst: *ir.Inst.Add, comptime arch: std.Target.Cpu.Arch) !MCValue {
324 const lhs = try self.resolveInst(inst.args.lhs);
325 const rhs = try self.resolveInst(inst.args.rhs);
326 switch (arch) {
327 .i386, .x86_64 => {
328 // const lhs_reg = try self.instAsReg(lhs);
329 // const rhs_reg = try self.instAsReg(rhs);
330 // const result = try self.allocateReg();
331
332 // try self.code.append(??);
333
334 // lhs_reg.release();
335 // rhs_reg.release();
336 return self.fail(inst.base.src, "TODO implement register allocation", .{});
337 },
198338 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),
199339 }
200340 }
201341
202 fn genArg(self: *Function, src: usize) !MCValue {
203 switch (self.target.cpu.arch) {
204 else => return self.fail(src, "TODO implement function parameters for {}", .{self.target.cpu.arch}),
205 }
206 return .none;
342 fn genArg(self: *Function, inst: *ir.Inst.Arg) !MCValue {
343 return self.args[inst.args.index];
207344 }
208345
209 fn genBreakpoint(self: *Function, src: usize) !MCValue {
210 switch (self.target.cpu.arch) {
346 fn genBreakpoint(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch) !MCValue {
347 switch (arch) {
211348 .i386, .x86_64 => {
212349 try self.code.append(0xcc); // int3
213350 },
......@@ -216,8 +353,8 @@ const Function = struct {
216353 return .none;
217354 }
218355
219 fn genCall(self: *Function, inst: *ir.Inst.Call) !MCValue {
220 switch (self.target.cpu.arch) {
356 fn genCall(self: *Function, inst: *ir.Inst.Call, comptime arch: std.Target.Cpu.Arch) !MCValue {
357 switch (arch) {
221358 .x86_64, .i386 => {
222359 if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| {
223360 if (inst.args.args.len != 0) {
......@@ -251,11 +388,11 @@ const Function = struct {
251388 }
252389 }
253390
254 fn ret(self: *Function, src: usize, mcv: MCValue) !MCValue {
391 fn ret(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch, mcv: MCValue) !MCValue {
255392 if (mcv != .none) {
256393 return self.fail(src, "TODO implement return with non-void operand", .{});
257394 }
258 switch (self.target.cpu.arch) {
395 switch (arch) {
259396 .i386, .x86_64 => {
260397 try self.code.append(0xc3); // ret
261398 },
......@@ -264,43 +401,43 @@ const Function = struct {
264401 return .unreach;
265402 }
266403
267 fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue {
404 fn genRet(self: *Function, inst: *ir.Inst.Ret, comptime arch: std.Target.Cpu.Arch) !MCValue {
268405 const operand = try self.resolveInst(inst.args.operand);
269 return self.ret(inst.base.src, operand);
406 return self.ret(inst.base.src, arch, operand);
270407 }
271408
272 fn genRetVoid(self: *Function, inst: *ir.Inst.RetVoid) !MCValue {
273 return self.ret(inst.base.src, .none);
409 fn genRetVoid(self: *Function, inst: *ir.Inst.RetVoid, comptime arch: std.Target.Cpu.Arch) !MCValue {
410 return self.ret(inst.base.src, arch, .none);
274411 }
275412
276 fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue {
277 switch (self.target.cpu.arch) {
413 fn genCmp(self: *Function, inst: *ir.Inst.Cmp, comptime arch: std.Target.Cpu.Arch) !MCValue {
414 switch (arch) {
278415 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
279416 }
280417 }
281418
282 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr) !MCValue {
283 switch (self.target.cpu.arch) {
419 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {
420 switch (arch) {
284421 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),
285422 }
286423 }
287424
288 fn genIsNull(self: *Function, inst: *ir.Inst.IsNull) !MCValue {
289 switch (self.target.cpu.arch) {
425 fn genIsNull(self: *Function, inst: *ir.Inst.IsNull, comptime arch: std.Target.Cpu.Arch) !MCValue {
426 switch (arch) {
290427 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),
291428 }
292429 }
293430
294 fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull) !MCValue {
431 fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull, comptime arch: std.Target.Cpu.Arch) !MCValue {
295432 // Here you can specialize this instruction if it makes sense to, otherwise the default
296433 // will call genIsNull and invert the result.
297 switch (self.target.cpu.arch) {
434 switch (arch) {
298435 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
299436 }
300437 }
301438
302 fn genRelativeFwdJump(self: *Function, src: usize, amount: u32) !void {
303 switch (self.target.cpu.arch) {
439 fn genRelativeFwdJump(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch, amount: u32) !void {
440 switch (arch) {
304441 .i386, .x86_64 => {
305442 // TODO x86 treats the operands as signed
306443 if (amount <= std.math.maxInt(u8)) {
......@@ -318,70 +455,13 @@ const Function = struct {
318455 }
319456 }
320457
321 fn genBlock(self: *Function, inst: *ir.Inst.Block) !MCValue {
322 switch (self.target.cpu.arch) {
458 fn genBlock(self: *Function, inst: *ir.Inst.Block, comptime arch: std.Target.Cpu.Arch) !MCValue {
459 switch (arch) {
323460 else => return self.fail(inst.base.src, "TODO implement codegen Block for {}", .{self.target.cpu.arch}),
324461 }
325462 }
326463
327 fn genAsm(self: *Function, inst: *ir.Inst.Assembly) !MCValue {
328 // TODO convert to inline function
329 switch (self.target.cpu.arch) {
330 .arm => return self.genAsmArch(.arm, inst),
331 .armeb => return self.genAsmArch(.armeb, inst),
332 .aarch64 => return self.genAsmArch(.aarch64, inst),
333 .aarch64_be => return self.genAsmArch(.aarch64_be, inst),
334 .aarch64_32 => return self.genAsmArch(.aarch64_32, inst),
335 .arc => return self.genAsmArch(.arc, inst),
336 .avr => return self.genAsmArch(.avr, inst),
337 .bpfel => return self.genAsmArch(.bpfel, inst),
338 .bpfeb => return self.genAsmArch(.bpfeb, inst),
339 .hexagon => return self.genAsmArch(.hexagon, inst),
340 .mips => return self.genAsmArch(.mips, inst),
341 .mipsel => return self.genAsmArch(.mipsel, inst),
342 .mips64 => return self.genAsmArch(.mips64, inst),
343 .mips64el => return self.genAsmArch(.mips64el, inst),
344 .msp430 => return self.genAsmArch(.msp430, inst),
345 .powerpc => return self.genAsmArch(.powerpc, inst),
346 .powerpc64 => return self.genAsmArch(.powerpc64, inst),
347 .powerpc64le => return self.genAsmArch(.powerpc64le, inst),
348 .r600 => return self.genAsmArch(.r600, inst),
349 .amdgcn => return self.genAsmArch(.amdgcn, inst),
350 .riscv32 => return self.genAsmArch(.riscv32, inst),
351 .riscv64 => return self.genAsmArch(.riscv64, inst),
352 .sparc => return self.genAsmArch(.sparc, inst),
353 .sparcv9 => return self.genAsmArch(.sparcv9, inst),
354 .sparcel => return self.genAsmArch(.sparcel, inst),
355 .s390x => return self.genAsmArch(.s390x, inst),
356 .tce => return self.genAsmArch(.tce, inst),
357 .tcele => return self.genAsmArch(.tcele, inst),
358 .thumb => return self.genAsmArch(.thumb, inst),
359 .thumbeb => return self.genAsmArch(.thumbeb, inst),
360 .i386 => return self.genAsmArch(.i386, inst),
361 .x86_64 => return self.genAsmArch(.x86_64, inst),
362 .xcore => return self.genAsmArch(.xcore, inst),
363 .nvptx => return self.genAsmArch(.nvptx, inst),
364 .nvptx64 => return self.genAsmArch(.nvptx64, inst),
365 .le32 => return self.genAsmArch(.le32, inst),
366 .le64 => return self.genAsmArch(.le64, inst),
367 .amdil => return self.genAsmArch(.amdil, inst),
368 .amdil64 => return self.genAsmArch(.amdil64, inst),
369 .hsail => return self.genAsmArch(.hsail, inst),
370 .hsail64 => return self.genAsmArch(.hsail64, inst),
371 .spir => return self.genAsmArch(.spir, inst),
372 .spir64 => return self.genAsmArch(.spir64, inst),
373 .kalimba => return self.genAsmArch(.kalimba, inst),
374 .shave => return self.genAsmArch(.shave, inst),
375 .lanai => return self.genAsmArch(.lanai, inst),
376 .wasm32 => return self.genAsmArch(.wasm32, inst),
377 .wasm64 => return self.genAsmArch(.wasm64, inst),
378 .renderscript32 => return self.genAsmArch(.renderscript32, inst),
379 .renderscript64 => return self.genAsmArch(.renderscript64, inst),
380 .ve => return self.genAsmArch(.ve, inst),
381 }
382 }
383
384 fn genAsmArch(self: *Function, comptime arch: Target.Cpu.Arch, inst: *ir.Inst.Assembly) !MCValue {
464 fn genAsm(self: *Function, inst: *ir.Inst.Assembly, comptime arch: Target.Cpu.Arch) !MCValue {
385465 if (arch != .x86_64 and arch != .i386) {
386466 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
387467 }
......@@ -607,6 +687,9 @@ const Function = struct {
607687 }
608688 }
609689 },
690 .stack_offset => |off| {
691 return self.fail(src, "TODO implement genSetReg for stack variables", .{});
692 }
610693 },
611694 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),
612695 }
src-self-hosted/codegen/x86_64.zig+26-4
......@@ -1,20 +1,21 @@
1const Type = @import("../Type.zig");
2
13// zig fmt: off
24
3/// Definitions of all of the x64 registers. The order is very, very important.
5/// Definitions of all of the x64 registers. The order is semantically meaningful.
46/// The registers are defined such that IDs go in descending order of 64-bit,
57/// 32-bit, 16-bit, and then 8-bit, and each set contains exactly sixteen
6/// registers. This results in some very, very useful properties:
8/// registers. This results in some useful properties:
79///
810/// Any 64-bit register can be turned into its 32-bit form by adding 16, and
911/// vice versa. This also works between 32-bit and 16-bit forms. With 8-bit, it
10/// works for all except for sp, bp, si, and di, which don't *have* an 8-bit
12/// works for all except for sp, bp, si, and di, which do *not* have an 8-bit
1113/// form.
1214///
1315/// If (register & 8) is set, the register is extended.
1416///
1517/// The ID can be easily determined by figuring out what range the register is
1618/// in, and then subtracting the base.
17///
1819pub const Register = enum(u8) {
1920 // 0 through 15, 64-bit registers. 8-15 are extended.
2021 // id is just the int value.
......@@ -67,3 +68,24 @@ pub const Register = enum(u8) {
6768};
6869
6970// zig fmt: on
71
72/// After argument values have been computed, they are placed either in registers
73/// or pushed on the stack. The way values are passed depends on the class.
74pub const ParameterClass = enum {
75 /// Integral types that fit into one of the general purpose registers.
76 integer,
77 /// Types that fit into a vector register.
78 sse,
79 /// Types that fit into a vector register and can be passed
80 /// and returned in the upper bytes of it.
81 sse_up,
82 /// Types that will be returned via the x87FPU.
83 x87,
84 /// Types that will be returned via the x87FPU and can be passed and returned
85 /// in the upper bytes of it.
86 x87_up,
87 /// Types that will be returned via the x87FPU.
88 complex_x87,
89 /// Types that will be passed and returned in mem-ory via the stack.
90 memory,
91};
src-self-hosted/type.zig+70
......@@ -535,6 +535,76 @@ pub const Type = extern union {
535535 };
536536 }
537537
538 /// Asserts the type has the ABI size already resolved.
539 pub fn abiSize(self: Type, target: Target) u64 {
540 return switch (self.tag()) {
541 .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer
542 .fn_void_no_args => unreachable, // represents machine code; not a pointer
543 .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer
544 .fn_ccc_void_no_args => unreachable, // represents machine code; not a pointer
545 .function => unreachable, // represents machine code; not a pointer
546 .c_void => unreachable,
547 .void => unreachable,
548 .type => unreachable,
549 .comptime_int => unreachable,
550 .comptime_float => unreachable,
551 .noreturn => unreachable,
552 .@"null" => unreachable,
553 .@"undefined" => unreachable,
554
555 .u8,
556 .i8,
557 .bool,
558 => return 1,
559
560 .array_u8_sentinel_0 => @fieldParentPtr(Payload.Array_u8_Sentinel0, "base", self.ptr_otherwise).len,
561 .array => {
562 const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
563 const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target));
564 return payload.len * elem_size;
565 },
566 .i16, .u16 => return 2,
567 .i32, .u32 => return 4,
568 .i64, .u64 => return 8,
569
570 .isize,
571 .usize,
572 .single_const_pointer_to_comptime_int,
573 .const_slice_u8,
574 .single_const_pointer,
575 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
576
577 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
578 .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
579 .c_int => return @divExact(CType.int.sizeInBits(target), 8),
580 .c_uint => return @divExact(CType.uint.sizeInBits(target), 8),
581 .c_long => return @divExact(CType.long.sizeInBits(target), 8),
582 .c_ulong => return @divExact(CType.ulong.sizeInBits(target), 8),
583 .c_longlong => return @divExact(CType.longlong.sizeInBits(target), 8),
584 .c_ulonglong => return @divExact(CType.ulonglong.sizeInBits(target), 8),
585
586 .f16 => return 2,
587 .f32 => return 4,
588 .f64 => return 8,
589 .f128 => return 16,
590 .c_longdouble => return 16,
591
592 .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type
593
594
595 .int_signed, .int_unsigned => {
596 const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
597 pl.bits
598 else if (self.cast(Payload.IntUnsigned)) |pl|
599 pl.bits
600 else
601 unreachable;
602
603 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
604 },
605 };
606 }
607
538608 pub fn isSinglePointer(self: Type) bool {
539609 return switch (self.tag()) {
540610 .u8,