github.com/llir/llvm@v0.3.6/ir/terminator.go (about) 1 package ir 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/llir/llvm/ir/constant" 8 "github.com/llir/llvm/ir/enum" 9 "github.com/llir/llvm/ir/types" 10 "github.com/llir/llvm/ir/value" 11 ) 12 13 // === [ Terminators ] ========================================================= 14 15 // Terminator is an LLVM IR terminator instruction (a control flow instruction). 16 // 17 // A Terminator has one of the following underlying types. 18 // 19 // Terminators 20 // 21 // https://llvm.org/docs/LangRef.html#terminator-instructions 22 // 23 // *ir.TermRet // https://pkg.go.dev/github.com/llir/llvm/ir#TermRet 24 // *ir.TermBr // https://pkg.go.dev/github.com/llir/llvm/ir#TermBr 25 // *ir.TermCondBr // https://pkg.go.dev/github.com/llir/llvm/ir#TermCondBr 26 // *ir.TermSwitch // https://pkg.go.dev/github.com/llir/llvm/ir#TermSwitch 27 // *ir.TermIndirectBr // https://pkg.go.dev/github.com/llir/llvm/ir#TermIndirectBr 28 // *ir.TermInvoke // https://pkg.go.dev/github.com/llir/llvm/ir#TermInvoke 29 // *ir.TermCallBr // https://pkg.go.dev/github.com/llir/llvm/ir#TermCallBr 30 // *ir.TermResume // https://pkg.go.dev/github.com/llir/llvm/ir#TermResume 31 // *ir.TermCatchSwitch // https://pkg.go.dev/github.com/llir/llvm/ir#TermCatchSwitch 32 // *ir.TermCatchRet // https://pkg.go.dev/github.com/llir/llvm/ir#TermCatchRet 33 // *ir.TermCleanupRet // https://pkg.go.dev/github.com/llir/llvm/ir#TermCleanupRet 34 // *ir.TermUnreachable // https://pkg.go.dev/github.com/llir/llvm/ir#TermUnreachable 35 type Terminator interface { 36 LLStringer 37 // Succs returns the successor basic blocks of the terminator. 38 Succs() []*Block 39 // Terminator implements the value.User interface. 40 value.User 41 } 42 43 // --- [ ret ] ----------------------------------------------------------------- 44 45 // TermRet is an LLVM IR ret terminator. 46 type TermRet struct { 47 // Return value; or nil if void return. 48 X value.Value 49 50 // extra. 51 52 // (optional) Metadata. 53 Metadata 54 } 55 56 // NewRet returns a new ret terminator based on the given return value. A nil 57 // return value indicates a void return. 58 func NewRet(x value.Value) *TermRet { 59 return &TermRet{X: x} 60 } 61 62 // Succs returns the successor basic blocks of the terminator. 63 func (*TermRet) Succs() []*Block { 64 // no successors. 65 return nil 66 } 67 68 // Operands returns a mutable list of operands of the given terminator. 69 func (term *TermRet) Operands() []*value.Value { 70 if term.X != nil { 71 return []*value.Value{&term.X} 72 } 73 return nil 74 } 75 76 // LLString returns the LLVM syntax representation of the terminator. 77 // 78 // Void return instruction. 79 // 80 // 'ret' XTyp=VoidType Metadata=(',' MetadataAttachment)+? 81 // 82 // Value return instruction. 83 // 84 // 'ret' XTyp=ConcreteType X=Value Metadata=(',' MetadataAttachment)+? 85 func (term *TermRet) LLString() string { 86 buf := &strings.Builder{} 87 if term.X == nil { 88 buf.WriteString("ret void") 89 } else { 90 fmt.Fprintf(buf, "ret %s", term.X) 91 } 92 for _, md := range term.Metadata { 93 fmt.Fprintf(buf, ", %s", md) 94 } 95 return buf.String() 96 } 97 98 // --- [ br ] ------------------------------------------------------------------ 99 100 // TermBr is an unconditional LLVM IR br terminator. 101 type TermBr struct { 102 // Target branch. 103 Target value.Value // *ir.Block 104 105 // extra. 106 107 // Successor basic blocks of the terminator. 108 Successors []*Block 109 // (optional) Metadata. 110 Metadata 111 } 112 113 // NewBr returns a new unconditional br terminator based on the given target 114 // basic block. 115 func NewBr(target *Block) *TermBr { 116 return &TermBr{Target: target} 117 } 118 119 // Succs returns the successor basic blocks of the terminator. 120 func (term *TermBr) Succs() []*Block { 121 // Cache successors if not present. 122 if term.Successors == nil { 123 term.Successors = []*Block{term.Target.(*Block)} 124 } 125 return term.Successors 126 } 127 128 // Operands returns a mutable list of operands of the given terminator. 129 func (term *TermBr) Operands() []*value.Value { 130 return []*value.Value{&term.Target} 131 } 132 133 // LLString returns the LLVM syntax representation of the terminator. 134 // 135 // 'br' Target=Label Metadata=(',' MetadataAttachment)+? 136 func (term *TermBr) LLString() string { 137 buf := &strings.Builder{} 138 fmt.Fprintf(buf, "br %s", term.Target) 139 for _, md := range term.Metadata { 140 fmt.Fprintf(buf, ", %s", md) 141 } 142 return buf.String() 143 } 144 145 // --- [ conditional br ] ------------------------------------------------------ 146 147 // TermCondBr is a conditional LLVM IR br terminator. 148 type TermCondBr struct { 149 // Branching condition. 150 Cond value.Value 151 // True condition target branch. 152 TargetTrue value.Value // *ir.Block 153 // False condition target branch. 154 TargetFalse value.Value // *ir.Block 155 156 // extra. 157 158 // Successor basic blocks of the terminator. 159 Successors []*Block 160 // (optional) Metadata. 161 Metadata 162 } 163 164 // NewCondBr returns a new conditional br terminator based on the given 165 // branching condition and conditional target basic blocks. 166 func NewCondBr(cond value.Value, targetTrue, targetFalse *Block) *TermCondBr { 167 return &TermCondBr{Cond: cond, TargetTrue: targetTrue, TargetFalse: targetFalse} 168 } 169 170 // Succs returns the successor basic blocks of the terminator. 171 func (term *TermCondBr) Succs() []*Block { 172 // Cache successors if not present. 173 if term.Successors == nil { 174 term.Successors = []*Block{term.TargetTrue.(*Block), term.TargetFalse.(*Block)} 175 } 176 return term.Successors 177 } 178 179 // Operands returns a mutable list of operands of the given terminator. 180 func (term *TermCondBr) Operands() []*value.Value { 181 return []*value.Value{&term.Cond, &term.TargetTrue, &term.TargetFalse} 182 } 183 184 // LLString returns the LLVM syntax representation of the terminator. 185 // 186 // 'br' CondTyp=IntType Cond=Value ',' TargetTrue=Label ',' TargetFalse=Label Metadata=(',' MetadataAttachment)+? 187 func (term *TermCondBr) LLString() string { 188 buf := &strings.Builder{} 189 fmt.Fprintf(buf, "br %s, %s, %s", term.Cond, term.TargetTrue, term.TargetFalse) 190 for _, md := range term.Metadata { 191 fmt.Fprintf(buf, ", %s", md) 192 } 193 return buf.String() 194 } 195 196 // --- [ switch ] -------------------------------------------------------------- 197 198 // TermSwitch is an LLVM IR switch terminator. 199 type TermSwitch struct { 200 // Control variable. 201 X value.Value 202 // Default target branch. 203 TargetDefault value.Value // *ir.Block 204 // Switch cases. 205 Cases []*Case 206 207 // extra. 208 209 // Successor basic blocks of the terminator. 210 Successors []*Block 211 // (optional) Metadata. 212 Metadata 213 } 214 215 // NewSwitch returns a new switch terminator based on the given control 216 // variable, default target basic block and switch cases. 217 func NewSwitch(x value.Value, targetDefault *Block, cases ...*Case) *TermSwitch { 218 return &TermSwitch{X: x, TargetDefault: targetDefault, Cases: cases} 219 } 220 221 // Succs returns the successor basic blocks of the terminator. 222 func (term *TermSwitch) Succs() []*Block { 223 // Cache successors if not present. 224 if term.Successors == nil { 225 succs := make([]*Block, 0, 1+len(term.Cases)) 226 succs = append(succs, term.TargetDefault.(*Block)) 227 for _, c := range term.Cases { 228 succs = append(succs, c.Target.(*Block)) 229 } 230 term.Successors = succs 231 } 232 return term.Successors 233 } 234 235 // Operands returns a mutable list of operands of the given terminator. 236 func (term *TermSwitch) Operands() []*value.Value { 237 ops := make([]*value.Value, 0, 1+1+2*len(term.Cases)) 238 ops = append(ops, &term.X) 239 ops = append(ops, &term.TargetDefault) 240 for i := range term.Cases { 241 ops = append(ops, &term.Cases[i].X) 242 ops = append(ops, &term.Cases[i].Target) 243 } 244 return ops 245 } 246 247 // LLString returns the LLVM syntax representation of the terminator. 248 // 249 // 'switch' X=TypeValue ',' Default=Label '[' Cases=Case* ']' Metadata=(',' MetadataAttachment)+? 250 func (term *TermSwitch) LLString() string { 251 buf := &strings.Builder{} 252 fmt.Fprintf(buf, "switch %s, %s [\n", term.X, term.TargetDefault) 253 for _, c := range term.Cases { 254 fmt.Fprintf(buf, "\t\t%s\n", c) 255 } 256 buf.WriteString("\t]") 257 for _, md := range term.Metadata { 258 fmt.Fprintf(buf, ", %s", md) 259 } 260 return buf.String() 261 } 262 263 // ~~~ [ Switch case ] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 264 265 // Case is a switch case. 266 type Case struct { 267 // Case comparand. 268 X value.Value // constant.Constant (integer constant or integer constant expression) 269 // Case target branch. 270 Target value.Value // *ir.Block 271 } 272 273 // NewCase returns a new switch case based on the given case comparand and 274 // target basic block. 275 func NewCase(x constant.Constant, target *Block) *Case { 276 return &Case{X: x, Target: target} 277 } 278 279 // String returns the string representation of the switch case. 280 func (c *Case) String() string { 281 // X=TypeConst ',' Target=Label 282 return fmt.Sprintf("%s, %s", c.X, c.Target) 283 } 284 285 // --- [ indirectbr ] ---------------------------------------------------------- 286 287 // TermIndirectBr is an LLVM IR indirectbr terminator. 288 type TermIndirectBr struct { 289 // Target address. 290 Addr value.Value // blockaddress 291 // Set of valid target basic blocks. 292 ValidTargets []value.Value // slice of *ir.Block 293 294 // extra. 295 296 // Successor basic blocks of the terminator. 297 Successors []*Block 298 // (optional) Metadata. 299 Metadata 300 } 301 302 // NewIndirectBr returns a new indirectbr terminator based on the given target 303 // address (derived from a blockaddress constant of type i8*) and set of valid 304 // target basic blocks. 305 func NewIndirectBr(addr value.Value, validTargets ...*Block) *TermIndirectBr { 306 // convert validTargets slice to []value.Value. 307 var targets []value.Value 308 for _, target := range validTargets { 309 targets = append(targets, target) 310 } 311 return &TermIndirectBr{Addr: addr, ValidTargets: targets} 312 } 313 314 // Succs returns the successor basic blocks of the terminator. 315 func (term *TermIndirectBr) Succs() []*Block { 316 // Cache successors if not present. 317 if term.Successors == nil { 318 // convert ValidTargets slice to []*ir.Block. 319 for _, target := range term.ValidTargets { 320 term.Successors = append(term.Successors, target.(*Block)) 321 } 322 } 323 return term.Successors 324 } 325 326 // Operands returns a mutable list of operands of the given terminator. 327 func (term *TermIndirectBr) Operands() []*value.Value { 328 ops := make([]*value.Value, 0, 1+len(term.ValidTargets)) 329 ops = append(ops, &term.Addr) 330 for i := range term.ValidTargets { 331 ops = append(ops, &term.ValidTargets[i]) 332 } 333 return ops 334 } 335 336 // LLString returns the LLVM syntax representation of the terminator. 337 // 338 // 'indirectbr' Addr=TypeValue ',' '[' ValidTargets=(Label separator ',')* ']' Metadata=(',' MetadataAttachment)+? 339 func (term *TermIndirectBr) LLString() string { 340 buf := &strings.Builder{} 341 fmt.Fprintf(buf, "indirectbr %s, [", term.Addr) 342 for i, target := range term.ValidTargets { 343 if i != 0 { 344 buf.WriteString(", ") 345 } 346 buf.WriteString(target.String()) 347 } 348 buf.WriteString("]") 349 for _, md := range term.Metadata { 350 fmt.Fprintf(buf, ", %s", md) 351 } 352 return buf.String() 353 } 354 355 // --- [ invoke ] -------------------------------------------------------------- 356 357 // TermInvoke is an LLVM IR invoke terminator. 358 type TermInvoke struct { 359 // Name of local variable associated with the result. 360 LocalIdent 361 // Invokee (callee function). 362 // TODO: specify the set of underlying types of Invokee. 363 Invokee value.Value 364 // Function arguments. 365 // 366 // Arg has one of the following underlying types: 367 // value.Value 368 // TODO: add metadata value? 369 Args []value.Value 370 // Normal control flow return point. 371 NormalRetTarget value.Value // *ir.Block 372 // Exception control flow return point. 373 ExceptionRetTarget value.Value // *ir.Block 374 375 // extra. 376 377 // Type of result produced by the terminator. 378 Typ types.Type 379 // Successor basic blocks of the terminator. 380 Successors []*Block 381 // (optional) Calling convention; zero if not present. 382 CallingConv enum.CallingConv 383 // (optional) Return attributes. 384 ReturnAttrs []ReturnAttribute 385 // (optional) Address space; zero if not present. 386 AddrSpace types.AddrSpace 387 // (optional) Function attributes. 388 FuncAttrs []FuncAttribute 389 // (optional) Operand bundles. 390 OperandBundles []*OperandBundle 391 // (optional) Metadata. 392 Metadata 393 } 394 395 // NewInvoke returns a new invoke terminator based on the given invokee, 396 // function arguments and control flow return points for normal and exceptional 397 // execution. 398 // 399 // TODO: specify the set of underlying types of invokee. 400 func NewInvoke(invokee value.Value, args []value.Value, normalRetTarget, exceptionRetTarget *Block) *TermInvoke { 401 term := &TermInvoke{Invokee: invokee, Args: args, NormalRetTarget: normalRetTarget, ExceptionRetTarget: exceptionRetTarget} 402 // Compute type. 403 term.Type() 404 return term 405 } 406 407 // String returns the LLVM syntax representation of the terminator as a type- 408 // value pair. 409 func (term *TermInvoke) String() string { 410 return fmt.Sprintf("%s %s", term.Type(), term.Ident()) 411 } 412 413 // Type returns the type of the terminator. 414 func (term *TermInvoke) Type() types.Type { 415 // Cache type if not present. 416 if term.Typ == nil { 417 sig := term.Sig() 418 term.Typ = sig.RetType 419 } 420 return term.Typ 421 } 422 423 // Succs returns the successor basic blocks of the terminator. 424 func (term *TermInvoke) Succs() []*Block { 425 // Cache successors if not present. 426 if term.Successors == nil { 427 term.Successors = []*Block{term.NormalRetTarget.(*Block), term.ExceptionRetTarget.(*Block)} 428 } 429 return term.Successors 430 } 431 432 // Operands returns a mutable list of operands of the given terminator. 433 func (term *TermInvoke) Operands() []*value.Value { 434 ops := make([]*value.Value, 0, 1+len(term.Args)+1+1) 435 ops = append(ops, &term.Invokee) 436 for i := range term.Args { 437 ops = append(ops, &term.Args[i]) 438 } 439 ops = append(ops, &term.NormalRetTarget) 440 ops = append(ops, &term.ExceptionRetTarget) 441 return ops 442 } 443 444 // LLString returns the LLVM syntax representation of the terminator. 445 // 446 // 'invoke' CallingConvopt ReturnAttrs=ReturnAttribute* AddrSpaceopt Typ=Type Invokee=Value '(' Args ')' FuncAttrs=FuncAttribute* OperandBundles=('[' (OperandBundle separator ',')+ ']')? 'to' NormalRetTarget=Label 'unwind' ExceptionRetTarget=Label Metadata=(',' MetadataAttachment)+? 447 func (term *TermInvoke) LLString() string { 448 buf := &strings.Builder{} 449 if !term.Type().Equal(types.Void) { 450 fmt.Fprintf(buf, "%s = ", term.Ident()) 451 } 452 buf.WriteString("invoke") 453 if term.CallingConv != enum.CallingConvNone { 454 fmt.Fprintf(buf, " %s", callingConvString(term.CallingConv)) 455 } 456 for _, attr := range term.ReturnAttrs { 457 fmt.Fprintf(buf, " %s", attr) 458 } 459 // (optional) Address space. 460 if term.AddrSpace != 0 { 461 fmt.Fprintf(buf, " %s", term.AddrSpace) 462 } 463 // Use function signature instead of return type for variadic functions. 464 invokeeType := term.Type() 465 if sig := term.Sig(); sig.Variadic { 466 invokeeType = sig 467 } 468 fmt.Fprintf(buf, " %s %s(", invokeeType, term.Invokee.Ident()) 469 for i, arg := range term.Args { 470 if i != 0 { 471 buf.WriteString(", ") 472 } 473 buf.WriteString(arg.String()) 474 } 475 buf.WriteString(")") 476 for _, attr := range term.FuncAttrs { 477 fmt.Fprintf(buf, " %s", attr) 478 } 479 if len(term.OperandBundles) > 0 { 480 buf.WriteString(" [ ") 481 for i, operandBundle := range term.OperandBundles { 482 if i != 0 { 483 buf.WriteString(", ") 484 } 485 buf.WriteString(operandBundle.String()) 486 } 487 buf.WriteString(" ]") 488 } 489 fmt.Fprintf(buf, "\n\t\tto %s unwind %s", term.NormalRetTarget, term.ExceptionRetTarget) 490 for _, md := range term.Metadata { 491 fmt.Fprintf(buf, ", %s", md) 492 } 493 return buf.String() 494 } 495 496 // Sig returns the function signature of the invokee. 497 func (term *TermInvoke) Sig() *types.FuncType { 498 t, ok := term.Invokee.Type().(*types.PointerType) 499 if !ok { 500 panic(fmt.Errorf("invalid invokee type; expected *types.PointerType, got %T", term.Invokee.Type())) 501 } 502 sig, ok := t.ElemType.(*types.FuncType) 503 if !ok { 504 panic(fmt.Errorf("invalid invokee type; expected *types.FuncType, got %T", t.ElemType)) 505 } 506 return sig 507 } 508 509 // --- [ callbr ] -------------------------------------------------------------- 510 511 // TermCallBr is an LLVM IR callbr terminator. 512 type TermCallBr struct { 513 // Name of local variable associated with the result. 514 LocalIdent 515 // Callee function. 516 // TODO: specify the set of underlying types of Callee. 517 Callee value.Value 518 // Function arguments. 519 // 520 // Arg has one of the following underlying types: 521 // value.Value 522 // TODO: add metadata value? 523 Args []value.Value 524 // Normal control flow return point. 525 NormalRetTarget value.Value // *ir.Block 526 // Other control flow return points. 527 OtherRetTargets []value.Value // slice of *ir.Block 528 529 // extra. 530 531 // Type of result produced by the terminator. 532 Typ types.Type 533 // Successor basic blocks of the terminator. 534 Successors []*Block 535 // (optional) Calling convention; zero if not present. 536 CallingConv enum.CallingConv 537 // (optional) Return attributes. 538 ReturnAttrs []ReturnAttribute 539 // (optional) Address space; zero if not present. 540 AddrSpace types.AddrSpace 541 // (optional) Function attributes. 542 FuncAttrs []FuncAttribute 543 // (optional) Operand bundles. 544 OperandBundles []*OperandBundle 545 // (optional) Metadata. 546 Metadata 547 } 548 549 // NewCallBr returns a new callbr terminator based on the given callee, function 550 // arguments and control flow return points for normal and exceptional 551 // execution. 552 // 553 // TODO: specify the set of underlying types of callee. 554 func NewCallBr(callee value.Value, args []value.Value, normalRetTarget *Block, otherRetTargets ...*Block) *TermCallBr { 555 // Convert otherRetTargets slice to []value.Value. 556 var otherRets []value.Value 557 for _, otherRetTarget := range otherRetTargets { 558 otherRets = append(otherRets, otherRetTarget) 559 } 560 term := &TermCallBr{Callee: callee, Args: args, NormalRetTarget: normalRetTarget, OtherRetTargets: otherRets} 561 // Compute type. 562 term.Type() 563 return term 564 } 565 566 // String returns the LLVM syntax representation of the terminator as a type- 567 // value pair. 568 func (term *TermCallBr) String() string { 569 return fmt.Sprintf("%s %s", term.Type(), term.Ident()) 570 } 571 572 // Type returns the type of the terminator. 573 func (term *TermCallBr) Type() types.Type { 574 // Cache type if not present. 575 if term.Typ == nil { 576 sig := term.Sig() 577 term.Typ = sig.RetType 578 } 579 return term.Typ 580 } 581 582 // Succs returns the successor basic blocks of the terminator. 583 func (term *TermCallBr) Succs() []*Block { 584 // Cache successors if not present. 585 if term.Successors == nil { 586 term.Successors = []*Block{term.NormalRetTarget.(*Block)} 587 // Convert OtherRetTargets slice to []*ir.Block. 588 for _, otherRetTarget := range term.OtherRetTargets { 589 term.Successors = append(term.Successors, otherRetTarget.(*Block)) 590 } 591 } 592 return term.Successors 593 } 594 595 // Operands returns a mutable list of operands of the given terminator. 596 func (term *TermCallBr) Operands() []*value.Value { 597 ops := make([]*value.Value, 0, 1+len(term.Args)+1+len(term.OtherRetTargets)) 598 ops = append(ops, &term.Callee) 599 for i := range term.Args { 600 ops = append(ops, &term.Args[i]) 601 } 602 ops = append(ops, &term.NormalRetTarget) 603 for i := range term.OtherRetTargets { 604 ops = append(ops, &term.OtherRetTargets[i]) 605 } 606 return ops 607 } 608 609 // LLString returns the LLVM syntax representation of the terminator. 610 // 611 // 'callbr' CallingConvopt ReturnAttrs=ReturnAttribute* AddrSpaceopt Typ=Type Callee=Value '(' Args ')' FuncAttrs=FuncAttribute* OperandBundles=('[' (OperandBundle separator ',')+ ']')? 'to' NormalRetTarget=Label '[' OtherRetTargets=(Label separator ',')* ']' Metadata=(',' MetadataAttachment)+? 612 func (term *TermCallBr) LLString() string { 613 buf := &strings.Builder{} 614 if !term.Type().Equal(types.Void) { 615 fmt.Fprintf(buf, "%s = ", term.Ident()) 616 } 617 buf.WriteString("callbr") 618 if term.CallingConv != enum.CallingConvNone { 619 fmt.Fprintf(buf, " %s", callingConvString(term.CallingConv)) 620 } 621 for _, attr := range term.ReturnAttrs { 622 fmt.Fprintf(buf, " %s", attr) 623 } 624 // (optional) Address space. 625 if term.AddrSpace != 0 { 626 fmt.Fprintf(buf, " %s", term.AddrSpace) 627 } 628 // Use function signature instead of return type for variadic functions. 629 calleeType := term.Type() 630 if sig := term.Sig(); sig.Variadic { 631 calleeType = sig 632 } 633 fmt.Fprintf(buf, " %s %s(", calleeType, term.Callee.Ident()) 634 for i, arg := range term.Args { 635 if i != 0 { 636 buf.WriteString(", ") 637 } 638 buf.WriteString(arg.String()) 639 } 640 buf.WriteString(")") 641 for _, attr := range term.FuncAttrs { 642 fmt.Fprintf(buf, " %s", attr) 643 } 644 if len(term.OperandBundles) > 0 { 645 buf.WriteString(" [ ") 646 for i, operandBundle := range term.OperandBundles { 647 if i != 0 { 648 buf.WriteString(", ") 649 } 650 buf.WriteString(operandBundle.String()) 651 } 652 buf.WriteString(" ]") 653 } 654 fmt.Fprintf(buf, "\n\t\tto %s [", term.NormalRetTarget) 655 for i, otherRetTarget := range term.OtherRetTargets { 656 if i != 0 { 657 buf.WriteString(", ") 658 } 659 buf.WriteString(otherRetTarget.String()) 660 } 661 buf.WriteString("]") 662 for _, md := range term.Metadata { 663 fmt.Fprintf(buf, ", %s", md) 664 } 665 return buf.String() 666 } 667 668 // Sig returns the function signature of the callee. 669 func (term *TermCallBr) Sig() *types.FuncType { 670 t, ok := term.Callee.Type().(*types.PointerType) 671 if !ok { 672 panic(fmt.Errorf("invalid callee type; expected *types.PointerType, got %T", term.Callee.Type())) 673 } 674 sig, ok := t.ElemType.(*types.FuncType) 675 if !ok { 676 panic(fmt.Errorf("invalid callee type; expected *types.FuncType, got %T", t.ElemType)) 677 } 678 return sig 679 } 680 681 // --- [ resume ] -------------------------------------------------------------- 682 683 // TermResume is an LLVM IR resume terminator. 684 type TermResume struct { 685 // Exception argument to propagate. 686 X value.Value 687 688 // extra. 689 690 // (optional) Metadata. 691 Metadata 692 } 693 694 // NewResume returns a new resume terminator based on the given exception 695 // argument to propagate. 696 func NewResume(x value.Value) *TermResume { 697 return &TermResume{X: x} 698 } 699 700 // Succs returns the successor basic blocks of the terminator. 701 func (term *TermResume) Succs() []*Block { 702 // no successors. 703 return nil 704 } 705 706 // Operands returns a mutable list of operands of the given terminator. 707 func (term *TermResume) Operands() []*value.Value { 708 return []*value.Value{&term.X} 709 } 710 711 // LLString returns the LLVM syntax representation of the terminator. 712 // 713 // 'resume' X=TypeValue Metadata=(',' MetadataAttachment)+? 714 func (term *TermResume) LLString() string { 715 buf := &strings.Builder{} 716 fmt.Fprintf(buf, "resume %s", term.X) 717 for _, md := range term.Metadata { 718 fmt.Fprintf(buf, ", %s", md) 719 } 720 return buf.String() 721 } 722 723 // --- [ catchswitch ] --------------------------------------------------------- 724 725 // TermCatchSwitch is an LLVM IR catchswitch terminator. 726 type TermCatchSwitch struct { 727 // Name of local variable associated with the result. 728 LocalIdent 729 // Parent exception pad. 730 ParentPad value.Value // ir.ExceptionPad 731 // Exception handlers. 732 Handlers []value.Value // []*ir.Block 733 // Optional default target basic block to transfer control flow to; or nil to 734 // unwind to caller function. 735 DefaultUnwindTarget value.Value // *ir.Block or nil 736 737 // extra. 738 739 // Successor basic blocks of the terminator. 740 Successors []*Block 741 // (optional) Metadata. 742 Metadata 743 } 744 745 // NewCatchSwitch returns a new catchswitch terminator based on the given parent 746 // exception pad, exception handlers and optional default unwind target. If 747 // defaultUnwindTarget is nil, catchswitch unwinds to caller function. 748 func NewCatchSwitch(parentPad ExceptionPad, handlers []*Block, defaultUnwindTarget *Block) *TermCatchSwitch { 749 // convert handlers slice to []value.Value. 750 var hs []value.Value 751 for _, handler := range handlers { 752 hs = append(hs, handler) 753 } 754 term := &TermCatchSwitch{ParentPad: parentPad, Handlers: hs} 755 if defaultUnwindTarget != nil { 756 // Note: since DefaultUnwindTarget is an interface we have to be careful 757 // with typed nil values (e.g. `(*ir.Block)(nil)`). This is to ensure that 758 // DefaultUnwindTarget is nil and not `{Type: ir.Block, Value: nil}`. 759 // 760 // ref: https://golang.org/doc/faq#nil_error 761 term.DefaultUnwindTarget = defaultUnwindTarget 762 } 763 return term 764 } 765 766 // String returns the LLVM syntax representation of the terminator as a type- 767 // value pair. 768 func (term *TermCatchSwitch) String() string { 769 return fmt.Sprintf("%s %s", term.Type(), term.Ident()) 770 } 771 772 // Type returns the type of the terminator. 773 func (term *TermCatchSwitch) Type() types.Type { 774 return types.Token 775 } 776 777 // Succs returns the successor basic blocks of the terminator. 778 func (term *TermCatchSwitch) Succs() []*Block { 779 // Cache successors if not present. 780 if term.Successors == nil { 781 // convert Handlers slice to []*ir.Block. 782 for _, handler := range term.Handlers { 783 term.Successors = append(term.Successors, handler.(*Block)) 784 } 785 if defaultUnwindTarget, ok := term.DefaultUnwindTarget.(*Block); ok { 786 term.Successors = append(term.Successors, defaultUnwindTarget) 787 } 788 } 789 return term.Successors 790 } 791 792 // Operands returns a mutable list of operands of the given terminator. 793 func (term *TermCatchSwitch) Operands() []*value.Value { 794 ops := make([]*value.Value, 0, 1+len(term.Handlers)+1) 795 ops = append(ops, &term.ParentPad) 796 for i := range term.Handlers { 797 ops = append(ops, &term.Handlers[i]) 798 } 799 if term.DefaultUnwindTarget != nil { 800 ops = append(ops, &term.DefaultUnwindTarget) 801 } 802 return ops 803 } 804 805 // LLString returns the LLVM syntax representation of the terminator. 806 // 807 // 'catchswitch' 'within' ParentPad=ExceptionPad '[' Handlers=Handlers ']' 'unwind' DefaultUnwindTarget=UnwindTarget Metadata=(',' MetadataAttachment)+? 808 func (term *TermCatchSwitch) LLString() string { 809 buf := &strings.Builder{} 810 fmt.Fprintf(buf, "%s = ", term.Ident()) 811 fmt.Fprintf(buf, "catchswitch within %s [", term.ParentPad.Ident()) 812 for i, handler := range term.Handlers { 813 if i != 0 { 814 buf.WriteString(", ") 815 } 816 buf.WriteString(handler.String()) 817 } 818 buf.WriteString("] unwind ") 819 if term.DefaultUnwindTarget != nil { 820 buf.WriteString(term.DefaultUnwindTarget.String()) 821 } else { 822 buf.WriteString("to caller") 823 } 824 for _, md := range term.Metadata { 825 fmt.Fprintf(buf, ", %s", md) 826 } 827 return buf.String() 828 } 829 830 // --- [ catchret ] ------------------------------------------------------------ 831 832 // TermCatchRet is an LLVM IR catchret terminator, which catches an in-flight 833 // exception from CatchPad and returns control flow to normal at Target. 834 type TermCatchRet struct { 835 // Exit catchpad. 836 CatchPad value.Value // *ir.InstCatchPad 837 // Target basic block to transfer control flow to. 838 Target value.Value // *ir.Block 839 840 // extra. 841 842 // Successor basic blocks of the terminator. 843 Successors []*Block 844 // (optional) Metadata. 845 Metadata 846 } 847 848 // NewCatchRet returns a new catchret terminator based on the given exit 849 // catchpad and target basic block. 850 func NewCatchRet(catchPad *InstCatchPad, target *Block) *TermCatchRet { 851 return &TermCatchRet{CatchPad: catchPad, Target: target} 852 } 853 854 // Succs returns the successor basic blocks of the terminator. 855 func (term *TermCatchRet) Succs() []*Block { 856 // Cache successors if not present. 857 if term.Successors == nil { 858 term.Successors = []*Block{term.Target.(*Block)} 859 } 860 return term.Successors 861 } 862 863 // Operands returns a mutable list of operands of the given terminator. 864 func (term *TermCatchRet) Operands() []*value.Value { 865 return []*value.Value{&term.CatchPad, &term.Target} 866 } 867 868 // LLString returns the LLVM syntax representation of the terminator. 869 // 870 // 'catchret' 'from' CatchPad=Value 'to' Target=Label Metadata=(',' MetadataAttachment)+? 871 func (term *TermCatchRet) LLString() string { 872 buf := &strings.Builder{} 873 fmt.Fprintf(buf, "catchret from %s to %s", term.CatchPad.Ident(), term.Target) 874 for _, md := range term.Metadata { 875 fmt.Fprintf(buf, ", %s", md) 876 } 877 return buf.String() 878 } 879 880 // --- [ cleanupret ] ---------------------------------------------------------- 881 882 // TermCleanupRet is an LLVM IR cleanupret terminator, which indicates that the 883 // personality function of a cleanuppad has finished and transfers control flow 884 // to an optional target basic block or unwinds to the caller function. 885 type TermCleanupRet struct { 886 // Exit cleanuppad. 887 CleanupPad value.Value // *ir.InstCleanupPad 888 // Optional target basic block to transfer control flow to; or nil to unwind 889 // to caller function. 890 UnwindTarget value.Value // *ir.Block or nil 891 892 // extra. 893 894 // Successor basic blocks of the terminator. 895 Successors []*Block 896 // (optional) Metadata. 897 Metadata 898 } 899 900 // NewCleanupRet returns a new cleanupret terminator based on the given exit 901 // cleanuppad and optional unwind target. If unwindTarget is nil, cleanupret 902 // unwinds to caller function. 903 func NewCleanupRet(cleanupPad *InstCleanupPad, unwindTarget *Block) *TermCleanupRet { 904 term := &TermCleanupRet{CleanupPad: cleanupPad} 905 if unwindTarget != nil { 906 // Note: since UnwindTarget is an interface we have to be careful 907 // with typed nil values (e.g. `(*ir.Block)(nil)`). This is to ensure that 908 // UnwindTarget is nil and not `{Type: ir.Block, Value: nil}`. 909 // 910 // ref: https://golang.org/doc/faq#nil_error 911 term.UnwindTarget = unwindTarget 912 } 913 return term 914 } 915 916 // Succs returns the successor basic blocks of the terminator. 917 func (term *TermCleanupRet) Succs() []*Block { 918 // Cache successors if not present. 919 if term.Successors == nil { 920 if unwindTarget, ok := term.UnwindTarget.(*Block); ok { 921 term.Successors = []*Block{unwindTarget} 922 } else { 923 term.Successors = []*Block{} 924 } 925 } 926 return term.Successors 927 } 928 929 // Operands returns a mutable list of operands of the given terminator. 930 func (term *TermCleanupRet) Operands() []*value.Value { 931 ops := []*value.Value{&term.CleanupPad} 932 if term.UnwindTarget != nil { 933 ops = append(ops, &term.UnwindTarget) 934 } 935 return ops 936 } 937 938 // LLString returns the LLVM syntax representation of the terminator. 939 // 940 // 'cleanupret' 'from' CleanupPad=Value 'unwind' UnwindTarget Metadata=(',' MetadataAttachment)+? 941 func (term *TermCleanupRet) LLString() string { 942 buf := &strings.Builder{} 943 fmt.Fprintf(buf, "cleanupret from %s unwind ", term.CleanupPad.Ident()) 944 if term.UnwindTarget != nil { 945 buf.WriteString(term.UnwindTarget.String()) 946 } else { 947 buf.WriteString("to caller") 948 } 949 for _, md := range term.Metadata { 950 fmt.Fprintf(buf, ", %s", md) 951 } 952 return buf.String() 953 } 954 955 // --- [ unreachable ] --------------------------------------------------------- 956 957 // TermUnreachable is an LLVM IR unreachable terminator. 958 type TermUnreachable struct { 959 // extra. 960 961 // (optional) Metadata. 962 Metadata 963 } 964 965 // NewUnreachable returns a new unreachable terminator. 966 func NewUnreachable() *TermUnreachable { 967 return &TermUnreachable{} 968 } 969 970 // Succs returns the successor basic blocks of the terminator. 971 func (term *TermUnreachable) Succs() []*Block { 972 // no successors. 973 return nil 974 } 975 976 // Operands returns a mutable list of operands of the given terminator. 977 func (term *TermUnreachable) Operands() []*value.Value { 978 return nil 979 } 980 981 // LLString returns the LLVM syntax representation of the terminator. 982 // 983 // 'unreachable' Metadata=(',' MetadataAttachment)+? 984 func (term *TermUnreachable) LLString() string { 985 buf := &strings.Builder{} 986 buf.WriteString("unreachable") 987 for _, md := range term.Metadata { 988 fmt.Fprintf(buf, ", %s", md) 989 } 990 return buf.String() 991 }