package llvm import "fmt" import "strings" type Terminator interface { IsTerminator () Instruction } type TerminatorBr struct { Target Value } func (*TerminatorBr) IsTerminator () { } func (this *TerminatorBr) LLString () string { buf := &strings.Builder{} fmt.Fprintf(buf, "br %s", this.Target) return buf.String() } func (this *Block) NewBr (target Value) *TerminatorBr { terminator := &TerminatorBr { Target: target } this.AddInstruction(terminator) return terminator } type TerminatorCallBr struct { InstructionCall NormalTarget Value OtherTargets []Value } func (*TerminatorCallBr) IsTerminator () { } func (this *TerminatorCallBr) LLString () string { buf := &strings.Builder{} if _, ok := this.Type().(*TypeVoid); !ok { fmt.Fprintf(buf, "%s = ", this.Name()) } buf.WriteString("callbr") // (optional) Address space. if this.AddressSpace != 0 { fmt.Fprintf(buf, " %s", this.AddressSpace) } // Use function signature instead of return type for variadic functions. calleeType := this.Type() if sig := this.Signature; sig.Variadic { calleeType = sig } fmt.Fprintf(buf, " %s %s(", calleeType, this.Callee) for index, arg := range this.Arguments { if index > 0 { buf.WriteString(", ") } buf.WriteString(arg.String()) } buf.WriteString(")") fmt.Fprintf(buf, "\n\t\tto %s [", this.NormalTarget) for i, otherRetTarget := range this.OtherTargets { if i != 0 { buf.WriteString(", ") } buf.WriteString(otherRetTarget.String()) } buf.WriteString("]") return buf.String() } func (this *Block) NewCallBr ( callee Value, signature *TypeFunction, args []Value, normalTarget Value, otherTargets ...Value, ) *TerminatorCallBr { terminator := &TerminatorCallBr { } terminator.Callee = callee terminator.Arguments = args terminator.NormalTarget = normalTarget terminator.OtherTargets = otherTargets terminator.Signature = signature terminator.Ty = signature.Return this.AddInstruction(terminator) return terminator } type TerminatorCatchRet struct { CatchPad Value Target Value } func (*TerminatorCatchRet) IsTerminator () { } func (this *TerminatorCatchRet) LLString () string { buf := &strings.Builder{} fmt.Fprintf(buf, "catchret from %s to %s", this.CatchPad.Name(), this.Target) return buf.String() } func (this *Block) NewCatchRet (catchPad Value, target Value) *TerminatorCatchRet { terminator := &TerminatorCatchRet { CatchPad: catchPad, Target: target, } this.AddInstruction(terminator) return terminator } type TerminatorCatchSwitch struct { Register ParentPad Value Handlers []Value DefaultUnwindTarget Value } func (*TerminatorCatchSwitch) IsTerminator () { } func (this *TerminatorCatchSwitch) LLString () string { buf := &strings.Builder{} fmt.Fprintf(buf, "%s = ", this.Name()) fmt.Fprintf(buf, "catchswitch within %s [", this.ParentPad.Name()) for i, handler := range this.Handlers { if i != 0 { buf.WriteString(", ") } buf.WriteString(handler.String()) } buf.WriteString("] unwind ") if this.DefaultUnwindTarget != nil { buf.WriteString(this.DefaultUnwindTarget.String()) } else { buf.WriteString("to caller") } return buf.String() } func (this *Block) NewCatchSwitch (parentPad Value, handlers []Value, defaultUnwindTarget Value) *TerminatorCatchSwitch { terminator := &TerminatorCatchSwitch { ParentPad: parentPad, Handlers: handlers, DefaultUnwindTarget: defaultUnwindTarget, } terminator.Ty = &TypeToken { } this.AddInstruction(terminator) return terminator } type TerminatorCleanupRet struct { CleanupPad Value UnwindTarget Value } func (*TerminatorCleanupRet) IsTerminator () { } func (this *TerminatorCleanupRet) LLString () string { buf := &strings.Builder{} fmt.Fprintf(buf, "cleanupret from %s unwind ", this.CleanupPad.Name()) if this.UnwindTarget != nil { buf.WriteString(this.UnwindTarget.String()) } else { buf.WriteString("to caller") } return buf.String() } func (this *Block) NewCleanupRet (cleanupPad, unwindTarget Value) *TerminatorCleanupRet { terminator := &TerminatorCleanupRet { CleanupPad: cleanupPad, UnwindTarget: unwindTarget, } this.AddInstruction(terminator) return terminator } type TerminatorCondBr struct { Condition Value True Value False Value } func (*TerminatorCondBr) IsTerminator () { } func (this *TerminatorCondBr) LLString () string { buf := &strings.Builder{} fmt.Fprintf(buf, "br %s, %s, %s", this.Condition, this.True, this.False) return buf.String() } func (this *Block) NewCondBr (condition Value, tru, fals Value) *TerminatorCondBr { terminator := &TerminatorCondBr { Condition: condition, True: tru, False: fals, } this.AddInstruction(terminator) return terminator } type TerminatorIndirectBr struct { Address Value ValidTargets []Value } func (*TerminatorIndirectBr) IsTerminator () { } func (this *TerminatorIndirectBr) LLString () string { buf := &strings.Builder{} fmt.Fprintf(buf, "indirectbr %s, [", this.Address) for i, target := range this.ValidTargets { if i != 0 { buf.WriteString(", ") } buf.WriteString(target.String()) } buf.WriteString("]") return buf.String() } func (this *Block) NewIndirectBr (address Value, validTargets ...Value) *TerminatorIndirectBr { terminator := &TerminatorIndirectBr { Address: address, ValidTargets: validTargets, } this.AddInstruction(terminator) return terminator } type TerminatorInvoke struct { Register Invokee Value Signature *TypeFunction Arguments []Value NormalTarget Value ExceptionTarget Value AddressSpace AddressSpace // TODO complete this } func (*TerminatorInvoke) IsTerminator () { } func (this *TerminatorInvoke) LLString () string { buf := &strings.Builder{} if _, ok := this.Type().(*TypeVoid); !ok { fmt.Fprintf(buf, "%s = ", this.Name()) } buf.WriteString("invoke") // (optional) Address space. if this.AddressSpace != 0 { fmt.Fprintf(buf, " %s", this.AddressSpace) } // Use function signature instead of return type for variadic functions. calleeType := this.Type() if sig := this.Signature; sig.Variadic { calleeType = sig } fmt.Fprintf(buf, " %s %s(", calleeType, this.Invokee) for index, arg := range this.Arguments { if index > 0 { buf.WriteString(", ") } buf.WriteString(arg.String()) } buf.WriteString(")") fmt.Fprintf(buf, "\n\t\tto %s unwind %s", this.NormalTarget, this.ExceptionTarget) return buf.String() } func (this *Block) NewInvoke ( invokee Value, signature *TypeFunction, args []Value, normalTarget Value, exceptionTarget Value, ) *TerminatorInvoke { terminator := &TerminatorInvoke { } terminator.Invokee = invokee terminator.Arguments = args terminator.NormalTarget = normalTarget terminator.ExceptionTarget = exceptionTarget terminator.Signature = signature terminator.Ty = signature.Return this.AddInstruction(terminator) return terminator } type TerminatorResume struct { X Value } func (*TerminatorResume) IsTerminator () { } func (this *TerminatorResume) LLString () string { buf := &strings.Builder{} fmt.Fprintf(buf, "resume %s", this.X) return buf.String() } func (this *Block) NewResume (x Value) *TerminatorResume { terminator := &TerminatorResume { X: x } this.AddInstruction(terminator) return terminator } type TerminatorRet struct { X Value } func (*TerminatorRet) IsTerminator () { } func (this *TerminatorRet) LLString () string { buf := &strings.Builder { } if this.X == nil { fmt.Fprintf(buf, "ret void") } else { fmt.Fprintf(buf, "ret %s", this.X) } return buf.String() } func (this *Block) NewRet (x Value) *TerminatorRet { terminator := &TerminatorRet { X: x } this.AddInstruction(terminator) return terminator } type Case struct { X Value Target Value } func (*Case) IsTerminator () { } func (this *Case) String () string { return fmt.Sprintf("%s, %s", this.X, this.Target) } type TerminatorSwitch struct { X Value Default Value Cases []*Case } func (*TerminatorSwitch) IsTerminator () { } func (this *TerminatorSwitch) LLString () string { buf := &strings.Builder{} fmt.Fprintf(buf, "switch %s, %s [\n", this.X, this.Default) for _, c := range this.Cases { fmt.Fprintf(buf, "\t\t%s\n", c) } buf.WriteString("\t]") return buf.String() } func (this *Block) NewSwitch (x, defaul Value, cases ...*Case) *TerminatorSwitch { terminator := &TerminatorSwitch { X: x, Default: defaul, Cases: cases, } this.AddInstruction(terminator) return terminator } type TerminatorUnreachable struct { } func (*TerminatorUnreachable) IsTerminator () { } func (this *TerminatorUnreachable) LLString () string { buf := &strings.Builder{} buf.WriteString("unreachable") return buf.String() } func (this *Block) NewUnreachable () *TerminatorUnreachable { terminator := &TerminatorUnreachable { } this.AddInstruction(terminator) return terminator }