blob e43397fa (14211B) - Raw
1 const std = @import("std"); 2 const Value = @import("value.zig").Value; 3 const Type = @import("type.zig").Type; 4 const Module = @import("Module.zig"); 5 const assert = std.debug.assert; 6 const codegen = @import("codegen.zig"); 7 const ast = std.zig.ast; 8 9 /// These are in-memory, analyzed instructions. See `zir.Inst` for the representation 10 /// of instructions that correspond to the ZIR text format. 11 /// This struct owns the `Value` and `Type` memory. When the struct is deallocated, 12 /// so are the `Value` and `Type`. The value of a constant must be copied into 13 /// a memory location for the value to survive after a const instruction. 14 pub const Inst = struct { 15 tag: Tag, 16 /// Each bit represents the index of an `Inst` parameter in the `args` field. 17 /// If a bit is set, it marks the end of the lifetime of the corresponding 18 /// instruction parameter. For example, 0b101 means that the first and 19 /// third `Inst` parameters' lifetimes end after this instruction, and will 20 /// not have any more following references. 21 /// The most significant bit being set means that the instruction itself is 22 /// never referenced, in other words its lifetime ends as soon as it finishes. 23 /// If bit 15 (0b1xxx_xxxx_xxxx_xxxx) is set, it means this instruction itself is unreferenced. 24 /// If bit 14 (0bx1xx_xxxx_xxxx_xxxx) is set, it means this is a special case and the 25 /// lifetimes of operands are encoded elsewhere. 26 deaths: DeathsInt = undefined, 27 ty: Type, 28 /// Byte offset into the source. 29 src: usize, 30 31 pub const DeathsInt = u16; 32 pub const DeathsBitIndex = std.math.Log2Int(DeathsInt); 33 pub const unreferenced_bit_index = @typeInfo(DeathsInt).Int.bits - 1; 34 pub const deaths_bits = unreferenced_bit_index - 1; 35 36 pub fn isUnused(self: Inst) bool { 37 return (self.deaths & (1 << unreferenced_bit_index)) != 0; 38 } 39 40 pub fn operandDies(self: Inst, index: DeathsBitIndex) bool { 41 assert(index < deaths_bits); 42 return @truncate(u1, self.deaths >> index) != 0; 43 } 44 45 pub fn clearOperandDeath(self: *Inst, index: DeathsBitIndex) void { 46 assert(index < deaths_bits); 47 self.deaths &= ~(@as(DeathsInt, 1) << index); 48 } 49 50 pub fn specialOperandDeaths(self: Inst) bool { 51 return (self.deaths & (1 << deaths_bits)) != 0; 52 } 53 54 pub const Tag = enum { 55 add, 56 alloc, 57 arg, 58 assembly, 59 bitand, 60 bitcast, 61 bitor, 62 block, 63 br, 64 breakpoint, 65 brvoid, 66 call, 67 cmp_lt, 68 cmp_lte, 69 cmp_eq, 70 cmp_gte, 71 cmp_gt, 72 cmp_neq, 73 condbr, 74 constant, 75 dbg_stmt, 76 isnonnull, 77 isnull, 78 iserr, 79 booland, 80 boolor, 81 /// Read a value from a pointer. 82 load, 83 loop, 84 ptrtoint, 85 ref, 86 ret, 87 retvoid, 88 varptr, 89 /// Write a value to a pointer. LHS is pointer, RHS is value. 90 store, 91 sub, 92 unreach, 93 not, 94 floatcast, 95 intcast, 96 unwrap_optional, 97 wrap_optional, 98 xor, 99 switchbr, 100 101 pub fn Type(tag: Tag) type { 102 return switch (tag) { 103 .alloc, 104 .retvoid, 105 .unreach, 106 .breakpoint, 107 .dbg_stmt, 108 => NoOp, 109 110 .ref, 111 .ret, 112 .bitcast, 113 .not, 114 .isnonnull, 115 .isnull, 116 .iserr, 117 .ptrtoint, 118 .floatcast, 119 .intcast, 120 .load, 121 .unwrap_optional, 122 .wrap_optional, 123 => UnOp, 124 125 .add, 126 .sub, 127 .cmp_lt, 128 .cmp_lte, 129 .cmp_eq, 130 .cmp_gte, 131 .cmp_gt, 132 .cmp_neq, 133 .store, 134 .booland, 135 .boolor, 136 .bitand, 137 .bitor, 138 .xor, 139 => BinOp, 140 141 .arg => Arg, 142 .assembly => Assembly, 143 .block => Block, 144 .br => Br, 145 .brvoid => BrVoid, 146 .call => Call, 147 .condbr => CondBr, 148 .constant => Constant, 149 .loop => Loop, 150 .varptr => VarPtr, 151 .switchbr => SwitchBr, 152 }; 153 } 154 155 pub fn fromCmpOp(op: std.math.CompareOperator) Tag { 156 return switch (op) { 157 .lt => .cmp_lt, 158 .lte => .cmp_lte, 159 .eq => .cmp_eq, 160 .gte => .cmp_gte, 161 .gt => .cmp_gt, 162 .neq => .cmp_neq, 163 }; 164 } 165 }; 166 167 /// Prefer `castTag` to this. 168 pub fn cast(base: *Inst, comptime T: type) ?*T { 169 if (@hasField(T, "base_tag")) { 170 return base.castTag(T.base_tag); 171 } 172 inline for (@typeInfo(Tag).Enum.fields) |field| { 173 const tag = @intToEnum(Tag, field.value); 174 if (base.tag == tag) { 175 if (T == tag.Type()) { 176 return @fieldParentPtr(T, "base", base); 177 } 178 return null; 179 } 180 } 181 unreachable; 182 } 183 184 pub fn castTag(base: *Inst, comptime tag: Tag) ?*tag.Type() { 185 if (base.tag == tag) { 186 return @fieldParentPtr(tag.Type(), "base", base); 187 } 188 return null; 189 } 190 191 pub fn Args(comptime T: type) type { 192 return std.meta.fieldInfo(T, .args).field_type; 193 } 194 195 /// Returns `null` if runtime-known. 196 pub fn value(base: *Inst) ?Value { 197 if (base.ty.onePossibleValue()) |opv| return opv; 198 199 const inst = base.castTag(.constant) orelse return null; 200 return inst.val; 201 } 202 203 pub fn cmpOperator(base: *Inst) ?std.math.CompareOperator { 204 return switch (base.tag) { 205 .cmp_lt => .lt, 206 .cmp_lte => .lte, 207 .cmp_eq => .eq, 208 .cmp_gte => .gte, 209 .cmp_gt => .gt, 210 .cmp_neq => .neq, 211 else => null, 212 }; 213 } 214 215 pub fn operandCount(base: *Inst) usize { 216 inline for (@typeInfo(Tag).Enum.fields) |field| { 217 const tag = @intToEnum(Tag, field.value); 218 if (tag == base.tag) { 219 return @fieldParentPtr(tag.Type(), "base", base).operandCount(); 220 } 221 } 222 unreachable; 223 } 224 225 pub fn getOperand(base: *Inst, index: usize) ?*Inst { 226 inline for (@typeInfo(Tag).Enum.fields) |field| { 227 const tag = @intToEnum(Tag, field.value); 228 if (tag == base.tag) { 229 return @fieldParentPtr(tag.Type(), "base", base).getOperand(index); 230 } 231 } 232 unreachable; 233 } 234 235 pub fn breakBlock(base: *Inst) ?*Block { 236 return switch (base.tag) { 237 .br => base.castTag(.br).?.block, 238 .brvoid => base.castTag(.brvoid).?.block, 239 else => null, 240 }; 241 } 242 243 pub const NoOp = struct { 244 base: Inst, 245 246 pub fn operandCount(self: *const NoOp) usize { 247 return 0; 248 } 249 pub fn getOperand(self: *const NoOp, index: usize) ?*Inst { 250 return null; 251 } 252 }; 253 254 pub const UnOp = struct { 255 base: Inst, 256 operand: *Inst, 257 258 pub fn operandCount(self: *const UnOp) usize { 259 return 1; 260 } 261 pub fn getOperand(self: *const UnOp, index: usize) ?*Inst { 262 if (index == 0) 263 return self.operand; 264 return null; 265 } 266 }; 267 268 pub const BinOp = struct { 269 base: Inst, 270 lhs: *Inst, 271 rhs: *Inst, 272 273 pub fn operandCount(self: *const BinOp) usize { 274 return 2; 275 } 276 pub fn getOperand(self: *const BinOp, index: usize) ?*Inst { 277 var i = index; 278 279 if (i < 1) 280 return self.lhs; 281 i -= 1; 282 283 if (i < 1) 284 return self.rhs; 285 i -= 1; 286 287 return null; 288 } 289 }; 290 291 pub const Arg = struct { 292 pub const base_tag = Tag.arg; 293 294 base: Inst, 295 name: [*:0]const u8, 296 297 pub fn operandCount(self: *const Arg) usize { 298 return 0; 299 } 300 pub fn getOperand(self: *const Arg, index: usize) ?*Inst { 301 return null; 302 } 303 }; 304 305 pub const Assembly = struct { 306 pub const base_tag = Tag.assembly; 307 308 base: Inst, 309 asm_source: []const u8, 310 is_volatile: bool, 311 output: ?[]const u8, 312 inputs: []const []const u8, 313 clobbers: []const []const u8, 314 args: []const *Inst, 315 316 pub fn operandCount(self: *const Assembly) usize { 317 return self.args.len; 318 } 319 pub fn getOperand(self: *const Assembly, index: usize) ?*Inst { 320 if (index < self.args.len) 321 return self.args[index]; 322 return null; 323 } 324 }; 325 326 pub const Block = struct { 327 pub const base_tag = Tag.block; 328 329 base: Inst, 330 body: Body, 331 /// This memory is reserved for codegen code to do whatever it needs to here. 332 codegen: codegen.BlockData = .{}, 333 334 pub fn operandCount(self: *const Block) usize { 335 return 0; 336 } 337 pub fn getOperand(self: *const Block, index: usize) ?*Inst { 338 return null; 339 } 340 }; 341 342 pub const Br = struct { 343 pub const base_tag = Tag.br; 344 345 base: Inst, 346 block: *Block, 347 operand: *Inst, 348 349 pub fn operandCount(self: *const Br) usize { 350 return 0; 351 } 352 pub fn getOperand(self: *const Br, index: usize) ?*Inst { 353 if (index == 0) 354 return self.operand; 355 return null; 356 } 357 }; 358 359 pub const BrVoid = struct { 360 pub const base_tag = Tag.brvoid; 361 362 base: Inst, 363 block: *Block, 364 365 pub fn operandCount(self: *const BrVoid) usize { 366 return 0; 367 } 368 pub fn getOperand(self: *const BrVoid, index: usize) ?*Inst { 369 return null; 370 } 371 }; 372 373 pub const Call = struct { 374 pub const base_tag = Tag.call; 375 376 base: Inst, 377 func: *Inst, 378 args: []const *Inst, 379 380 pub fn operandCount(self: *const Call) usize { 381 return self.args.len + 1; 382 } 383 pub fn getOperand(self: *const Call, index: usize) ?*Inst { 384 var i = index; 385 386 if (i < 1) 387 return self.func; 388 i -= 1; 389 390 if (i < self.args.len) 391 return self.args[i]; 392 i -= self.args.len; 393 394 return null; 395 } 396 }; 397 398 pub const CondBr = struct { 399 pub const base_tag = Tag.condbr; 400 401 base: Inst, 402 condition: *Inst, 403 then_body: Body, 404 else_body: Body, 405 /// Set of instructions whose lifetimes end at the start of one of the branches. 406 /// The `then` branch is first: `deaths[0..then_death_count]`. 407 /// The `else` branch is next: `(deaths + then_death_count)[0..else_death_count]`. 408 deaths: [*]*Inst = undefined, 409 then_death_count: u32 = 0, 410 else_death_count: u32 = 0, 411 412 pub fn operandCount(self: *const CondBr) usize { 413 return 1; 414 } 415 pub fn getOperand(self: *const CondBr, index: usize) ?*Inst { 416 var i = index; 417 418 if (i < 1) 419 return self.condition; 420 i -= 1; 421 422 return null; 423 } 424 pub fn thenDeaths(self: *const CondBr) []*Inst { 425 return self.deaths[0..self.then_death_count]; 426 } 427 pub fn elseDeaths(self: *const CondBr) []*Inst { 428 return (self.deaths + self.then_death_count)[0..self.else_death_count]; 429 } 430 }; 431 432 pub const Constant = struct { 433 pub const base_tag = Tag.constant; 434 435 base: Inst, 436 val: Value, 437 438 pub fn operandCount(self: *const Constant) usize { 439 return 0; 440 } 441 pub fn getOperand(self: *const Constant, index: usize) ?*Inst { 442 return null; 443 } 444 }; 445 446 pub const Loop = struct { 447 pub const base_tag = Tag.loop; 448 449 base: Inst, 450 body: Body, 451 452 pub fn operandCount(self: *const Loop) usize { 453 return 0; 454 } 455 pub fn getOperand(self: *const Loop, index: usize) ?*Inst { 456 return null; 457 } 458 }; 459 460 pub const VarPtr = struct { 461 pub const base_tag = Tag.varptr; 462 463 base: Inst, 464 variable: *Module.Var, 465 466 pub fn operandCount(self: *const VarPtr) usize { 467 return 0; 468 } 469 pub fn getOperand(self: *const VarPtr, index: usize) ?*Inst { 470 return null; 471 } 472 }; 473 474 pub const SwitchBr = struct { 475 pub const base_tag = Tag.switchbr; 476 477 base: Inst, 478 target_ptr: *Inst, 479 cases: []Case, 480 /// Set of instructions whose lifetimes end at the start of one of the cases. 481 /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... ]. 482 deaths: [*]*Inst = undefined, 483 else_index: u32 = 0, 484 else_deaths: u32 = 0, 485 else_body: Body, 486 487 pub const Case = struct { 488 item: Value, 489 body: Body, 490 index: u32 = 0, 491 deaths: u32 = 0, 492 }; 493 494 pub fn operandCount(self: *const SwitchBr) usize { 495 return 1; 496 } 497 pub fn getOperand(self: *const SwitchBr, index: usize) ?*Inst { 498 var i = index; 499 500 if (i < 1) 501 return self.target_ptr; 502 i -= 1; 503 504 return null; 505 } 506 pub fn caseDeaths(self: *const SwitchBr, case_index: usize) []*Inst { 507 const case = self.cases[case_index]; 508 return (self.deaths + case.index)[0..case.deaths]; 509 } 510 pub fn elseDeaths(self: *const SwitchBr) []*Inst { 511 return (self.deaths + self.else_index)[0..self.else_deaths]; 512 } 513 }; 514 }; 515 516 pub const Body = struct { 517 instructions: []*Inst, 518 };