1const std = @import("std"); 2const builtin = @import("builtin"); 3const mem = std.mem; 4const math = std.math; 5const assert = std.debug.assert; 6const Air = @import("Air.zig"); 7const Zir = @import("Zir.zig"); 8const Liveness = @import("Liveness.zig"); 9const Type = @import("type.zig").Type; 10const Value = @import("value.zig").Value; 11const TypedValue = @import("TypedValue.zig"); 12const link = @import("link.zig"); 13const Module = @import("Module.zig"); 14const Compilation = @import("Compilation.zig"); 15const ErrorMsg = Module.ErrorMsg; 16const Target = std.Target; 17const Allocator = mem.Allocator; 18const trace = @import("tracy.zig").trace; 19const DW = std.dwarf; 20const leb128 = std.leb; 21const log = std.log.scoped(.codegen); 22const build_options = @import("build_options"); 23const RegisterManager = @import("register_manager.zig").RegisterManager; 24 25pub const FnResult = union(enum) { 26 /// The `code` parameter passed to `generateSymbol` has the value appended. 27 appended: void, 28 fail: *ErrorMsg, 29}; 30pub const Result = union(enum) { 31 /// The `code` parameter passed to `generateSymbol` has the value appended. 32 appended: void, 33 /// The value is available externally, `code` is unused. 34 externally_managed: []const u8, 35 fail: *ErrorMsg, 36}; 37 38pub const GenerateSymbolError = error{ 39 OutOfMemory, 40 Overflow, 41 /// A Decl that this symbol depends on had a semantic analysis failure. 42 AnalysisFail, 43}; 44 45pub const DebugInfoOutput = union(enum) { 46 dwarf: struct { 47 dbg_line: *std.ArrayList(u8), 48 dbg_info: *std.ArrayList(u8), 49 dbg_info_type_relocs: *link.File.DbgInfoTypeRelocsTable, 50 }, 51 /// the plan9 debuginfo output is a bytecode with 4 opcodes 52 /// assume all numbers/variables are bytes 53 /// 0 w x y z -> interpret w x y z as a big-endian i32, and add it to the line offset 54 /// x when x < 65 -> add x to line offset 55 /// x when x < 129 -> subtract 64 from x and subtract it from the line offset 56 /// x -> subtract 129 from x, multiply it by the quanta of the instruction size 57 /// (1 on x86_64), and add it to the pc 58 /// after every opcode, add the quanta of the instruction size to the pc 59 plan9: struct { 60 /// the actual opcodes 61 dbg_line: *std.ArrayList(u8), 62 /// what line the debuginfo starts on 63 /// this helps because the linker might have to insert some opcodes to make sure that the line count starts at the right amount for the next decl 64 start_line: *?u32, 65 /// what the line count ends on after codegen 66 /// this helps because the linker might have to insert some opcodes to make sure that the line count starts at the right amount for the next decl 67 end_line: *u32, 68 /// the last pc change op 69 /// This is very useful for adding quanta 70 /// to it if its not actually the last one. 71 pcop_change_index: *?u32, 72 }, 73 none, 74}; 75 76pub fn generateFunction( 77 bin_file: *link.File, 78 src_loc: Module.SrcLoc, 79 func: *Module.Fn, 80 air: Air, 81 liveness: Liveness, 82 code: *std.ArrayList(u8), 83 debug_output: DebugInfoOutput, 84) GenerateSymbolError!FnResult { 85 switch (bin_file.options.target.cpu.arch) { 86 .wasm32 => unreachable, // has its own code path 87 .wasm64 => unreachable, // has its own code path 88 .arm, 89 .armeb, 90 => return @import("arch/arm/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), 91 .aarch64, 92 .aarch64_be, 93 .aarch64_32, 94 => return @import("arch/aarch64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), 95 //.arc => return Function(.arc).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 96 //.avr => return Function(.avr).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 97 //.bpfel => return Function(.bpfel).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 98 //.bpfeb => return Function(.bpfeb).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 99 //.hexagon => return Function(.hexagon).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 100 //.mips => return Function(.mips).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 101 //.mipsel => return Function(.mipsel).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 102 //.mips64 => return Function(.mips64).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 103 //.mips64el => return Function(.mips64el).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 104 //.msp430 => return Function(.msp430).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 105 //.powerpc => return Function(.powerpc).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 106 //.powerpc64 => return Function(.powerpc64).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 107 //.powerpc64le => return Function(.powerpc64le).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 108 //.r600 => return Function(.r600).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 109 //.amdgcn => return Function(.amdgcn).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 110 //.riscv32 => return Function(.riscv32).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 111 .riscv64 => return @import("arch/riscv64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), 112 //.sparc => return Function(.sparc).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 113 //.sparcv9 => return Function(.sparcv9).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 114 //.sparcel => return Function(.sparcel).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 115 //.s390x => return Function(.s390x).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 116 //.tce => return Function(.tce).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 117 //.tcele => return Function(.tcele).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 118 //.thumb => return Function(.thumb).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 119 //.thumbeb => return Function(.thumbeb).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 120 //.i386 => return Function(.i386).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 121 .x86_64 => return @import("arch/x86_64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output), 122 //.xcore => return Function(.xcore).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 123 //.nvptx => return Function(.nvptx).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 124 //.nvptx64 => return Function(.nvptx64).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 125 //.le32 => return Function(.le32).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 126 //.le64 => return Function(.le64).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 127 //.amdil => return Function(.amdil).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 128 //.amdil64 => return Function(.amdil64).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 129 //.hsail => return Function(.hsail).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 130 //.hsail64 => return Function(.hsail64).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 131 //.spir => return Function(.spir).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 132 //.spir64 => return Function(.spir64).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 133 //.kalimba => return Function(.kalimba).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 134 //.shave => return Function(.shave).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 135 //.lanai => return Function(.lanai).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 136 //.renderscript32 => return Function(.renderscript32).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 137 //.renderscript64 => return Function(.renderscript64).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 138 //.ve => return Function(.ve).generate(bin_file, src_loc, func, air, liveness, code, debug_output), 139 else => @panic("Backend architectures that don't have good support yet are commented out, to improve compilation performance. If you are interested in one of these other backends feel free to uncomment them. Eventually these will be completed, but stage1 is slow and a memory hog."), 140 } 141} 142 143pub fn generateSymbol( 144 bin_file: *link.File, 145 src_loc: Module.SrcLoc, 146 typed_value: TypedValue, 147 code: *std.ArrayList(u8), 148 debug_output: DebugInfoOutput, 149) GenerateSymbolError!Result { 150 const tracy = trace(@src()); 151 defer tracy.end(); 152 153 switch (typed_value.ty.zigTypeTag()) { 154 .Fn => { 155 return Result{ 156 .fail = try ErrorMsg.create( 157 bin_file.allocator, 158 src_loc, 159 "TODO implement generateSymbol function pointers", 160 .{}, 161 ), 162 }; 163 }, 164 .Array => { 165 // TODO populate .debug_info for the array 166 if (typed_value.val.castTag(.bytes)) |payload| { 167 if (typed_value.ty.sentinel()) |sentinel| { 168 try code.ensureUnusedCapacity(payload.data.len + 1); 169 code.appendSliceAssumeCapacity(payload.data); 170 switch (try generateSymbol(bin_file, src_loc, .{ 171 .ty = typed_value.ty.elemType(), 172 .val = sentinel, 173 }, code, debug_output)) { 174 .appended => return Result{ .appended = {} }, 175 .externally_managed => |slice| { 176 code.appendSliceAssumeCapacity(slice); 177 return Result{ .appended = {} }; 178 }, 179 .fail => |em| return Result{ .fail = em }, 180 } 181 } else { 182 return Result{ .externally_managed = payload.data }; 183 } 184 } 185 return Result{ 186 .fail = try ErrorMsg.create( 187 bin_file.allocator, 188 src_loc, 189 "TODO implement generateSymbol for more kinds of arrays", 190 .{}, 191 ), 192 }; 193 }, 194 .Pointer => switch (typed_value.ty.ptrSize()) { 195 .Slice => { 196 return Result{ 197 .fail = try ErrorMsg.create( 198 bin_file.allocator, 199 src_loc, 200 "TODO implement generateSymbol for slice {}", 201 .{typed_value.val}, 202 ), 203 }; 204 }, 205 else => { 206 // TODO populate .debug_info for the pointer 207 if (typed_value.val.castTag(.decl_ref)) |payload| { 208 const decl = payload.data; 209 if (decl.analysis != .complete) return error.AnalysisFail; 210 decl.alive = true; 211 // TODO handle the dependency of this symbol on the decl's vaddr. 212 // If the decl changes vaddr, then this symbol needs to get regenerated. 213 const vaddr = bin_file.getDeclVAddr(decl); 214 const endian = bin_file.options.target.cpu.arch.endian(); 215 switch (bin_file.options.target.cpu.arch.ptrBitWidth()) { 216 16 => { 217 try code.resize(2); 218 mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian); 219 }, 220 32 => { 221 try code.resize(4); 222 mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian); 223 }, 224 64 => { 225 try code.resize(8); 226 mem.writeInt(u64, code.items[0..8], vaddr, endian); 227 }, 228 else => unreachable, 229 } 230 return Result{ .appended = {} }; 231 } 232 return Result{ 233 .fail = try ErrorMsg.create( 234 bin_file.allocator, 235 src_loc, 236 "TODO implement generateSymbol for pointer {}", 237 .{typed_value.val}, 238 ), 239 }; 240 }, 241 }, 242 .Int => { 243 // TODO populate .debug_info for the integer 244 const endian = bin_file.options.target.cpu.arch.endian(); 245 const info = typed_value.ty.intInfo(bin_file.options.target); 246 if (info.bits <= 8) { 247 const x = @intCast(u8, typed_value.val.toUnsignedInt()); 248 try code.append(x); 249 return Result{ .appended = {} }; 250 } 251 if (info.bits > 64) { 252 return Result{ 253 .fail = try ErrorMsg.create( 254 bin_file.allocator, 255 src_loc, 256 "TODO implement generateSymbol for big ints ('{}')", 257 .{typed_value.ty}, 258 ), 259 }; 260 } 261 switch (info.signedness) { 262 .unsigned => { 263 if (info.bits <= 16) { 264 const x = @intCast(u16, typed_value.val.toUnsignedInt()); 265 mem.writeInt(u16, try code.addManyAsArray(2), x, endian); 266 } else if (info.bits <= 32) { 267 const x = @intCast(u32, typed_value.val.toUnsignedInt()); 268 mem.writeInt(u32, try code.addManyAsArray(4), x, endian); 269 } else { 270 const x = typed_value.val.toUnsignedInt(); 271 mem.writeInt(u64, try code.addManyAsArray(8), x, endian); 272 } 273 }, 274 .signed => { 275 if (info.bits <= 16) { 276 const x = @intCast(i16, typed_value.val.toSignedInt()); 277 mem.writeInt(i16, try code.addManyAsArray(2), x, endian); 278 } else if (info.bits <= 32) { 279 const x = @intCast(i32, typed_value.val.toSignedInt()); 280 mem.writeInt(i32, try code.addManyAsArray(4), x, endian); 281 } else { 282 const x = typed_value.val.toSignedInt(); 283 mem.writeInt(i64, try code.addManyAsArray(8), x, endian); 284 } 285 }, 286 } 287 return Result{ .appended = {} }; 288 }, 289 .Enum => { 290 // TODO populate .debug_info for the enum 291 var int_buffer: Value.Payload.U64 = undefined; 292 const int_val = typed_value.enumToInt(&int_buffer); 293 294 const target = bin_file.options.target; 295 const info = typed_value.ty.intInfo(target); 296 if (info.bits <= 8) { 297 const x = @intCast(u8, int_val.toUnsignedInt()); 298 try code.append(x); 299 return Result{ .appended = {} }; 300 } 301 if (info.bits > 64) { 302 return Result{ 303 .fail = try ErrorMsg.create( 304 bin_file.allocator, 305 src_loc, 306 "TODO implement generateSymbol for big int enums ('{}')", 307 .{typed_value.ty}, 308 ), 309 }; 310 } 311 const endian = target.cpu.arch.endian(); 312 switch (info.signedness) { 313 .unsigned => { 314 if (info.bits <= 16) { 315 const x = @intCast(u16, int_val.toUnsignedInt()); 316 mem.writeInt(u16, try code.addManyAsArray(2), x, endian); 317 } else if (info.bits <= 32) { 318 const x = @intCast(u32, int_val.toUnsignedInt()); 319 mem.writeInt(u32, try code.addManyAsArray(4), x, endian); 320 } else { 321 const x = int_val.toUnsignedInt(); 322 mem.writeInt(u64, try code.addManyAsArray(8), x, endian); 323 } 324 }, 325 .signed => { 326 if (info.bits <= 16) { 327 const x = @intCast(i16, int_val.toSignedInt()); 328 mem.writeInt(i16, try code.addManyAsArray(2), x, endian); 329 } else if (info.bits <= 32) { 330 const x = @intCast(i32, int_val.toSignedInt()); 331 mem.writeInt(i32, try code.addManyAsArray(4), x, endian); 332 } else { 333 const x = int_val.toSignedInt(); 334 mem.writeInt(i64, try code.addManyAsArray(8), x, endian); 335 } 336 }, 337 } 338 return Result{ .appended = {} }; 339 }, 340 .Bool => { 341 const x: u8 = @boolToInt(typed_value.val.toBool()); 342 try code.append(x); 343 return Result{ .appended = {} }; 344 }, 345 .Struct => { 346 const field_vals = typed_value.val.castTag(.@"struct").?.data; 347 _ = field_vals; // TODO write the fields for real 348 const target = bin_file.options.target; 349 const abi_size = try math.cast(usize, typed_value.ty.abiSize(target)); 350 try code.writer().writeByteNTimes(0xaa, abi_size); 351 return Result{ .appended = {} }; 352 }, 353 else => |t| { 354 return Result{ 355 .fail = try ErrorMsg.create( 356 bin_file.allocator, 357 src_loc, 358 "TODO implement generateSymbol for type '{s}'", 359 .{@tagName(t)}, 360 ), 361 }; 362 }, 363 } 364} 365