diff --git a/_lldb/llgo_plugin.py b/_lldb/llgo_plugin.py index 9e26741d..27ae8ad9 100644 --- a/_lldb/llgo_plugin.py +++ b/_lldb/llgo_plugin.py @@ -93,7 +93,7 @@ def print_all_variables(debugger: lldb.SBDebugger, _command: str, result: lldb.S frame = debugger.GetSelectedTarget().GetProcess( ).GetSelectedThread().GetSelectedFrame() - variables = frame.GetVariables(True, True, True, False) + variables = frame.GetVariables(True, True, True, True) output: List[str] = [] for var in variables: diff --git a/_lldb/test.py b/_lldb/test.py index 1739e318..6de2c11d 100644 --- a/_lldb/test.py +++ b/_lldb/test.py @@ -107,7 +107,7 @@ class LLDBDebugger: def get_all_variable_names(self) -> Set[str]: frame = self.process.GetSelectedThread().GetFrameAtIndex(0) - return set(var.GetName() for var in frame.GetVariables(True, True, True, False)) + return set(var.GetName() for var in frame.GetVariables(True, True, True, True)) def get_current_function_name(self) -> str: frame = self.process.GetSelectedThread().GetFrameAtIndex(0) diff --git a/cl/_testdata/debug/in.go b/cl/_testdata/debug/in.go index b1e48a0d..7f274e40 100644 --- a/cl/_testdata/debug/in.go +++ b/cl/_testdata/debug/in.go @@ -118,26 +118,33 @@ func FuncWithAllTypeParams( ) (int, error) { // Expected: // all variables: i8 i16 i32 i64 i u8 u16 u32 u64 u f32 f64 b c64 c128 slice arr arr2 s e f pf pi intr m c err fn - // i8: '\x01' - // i16: 2 // i32: 3 // i64: 4 // i: 5 - // u8: '\x06' - // u16: 7 // u32: 8 // u64: 9 // u: 10 // f32: 11 // f64: 12 - // b: true - // c64: complex64{real = 13, imag = 14} - // c128: complex128{real = 15, imag = 16} // slice: []int{21, 22, 23} // arr: [3]int{24, 25, 26} // arr2: [3]github.com/goplus/llgo/cl/_testdata/debug.E{{i = 27}, {i = 28}, {i = 29}} - // s: "hello" // e: github.com/goplus/llgo/cl/_testdata/debug.E{i = 30} + // i8: '\b' + // i16: 2 + // u8: '\x06' + // u16: 7 + // b: true + // c64: complex64{real = 13, imag = 14} + // c128: complex128{real = 15, imag = 16} + // s: "hello" + + // Expected(skip): + // i8: '\b' + // i16: 2 + // u8: '\x06' + // u16: 7 + // b: true println( i8, i16, i32, i64, i, u8, u16, u32, u64, u, f32, f64, b, @@ -352,6 +359,63 @@ func FuncStructPtrParams(t *TinyStruct, s *SmallStruct, m *MidStruct, b *BigStru println("done") } +func ScopeIf(branch int) { + a := 1 + // Expected: + // all variables: a branch + // a: 1 + if branch == 1 { + b := 2 + c := 3 + // Expected: + // all variables: a b c branch + // a: 1 + // b: 2 + // c: 3 + // branch: 1 + println(a, b, c) + } else { + c := 3 + d := 4 + // Expected: + // all variables: a c d branch + // a: 1 + // c: 3 + // d: 4 + // branch: 0 + println(c, d) + } + // Expected: + // all variables: a branch + // a: 1 + println("a:", a) +} + +func ScopeFor() { + a := 1 + for i := 0; i < 10; i++ { + switch i { + case 0: + println("i is 0") + // Expected: + // all variables: i a + // i: 0 + // a: 1 + println("i:", i) + case 1: + println("i is 1") + // Expected: + // all variables: i a + // i: 1 + // a: 1 + println("i:", i) + default: + println("i is", i) + } + } + println("a:", a) +} + func main() { FuncStructParams(TinyStruct{I: 1}, SmallStruct{I: 2, J: 3}, MidStruct{I: 4, J: 5, K: 6}, BigStruct{I: 7, J: 8, K: 9, L: 10, M: 11, N: 12, O: 13, P: 14, Q: 15, R: 16}) FuncStructPtrParams(&TinyStruct{I: 1}, &SmallStruct{I: 2, J: 3}, &MidStruct{I: 4, J: 5, K: 6}, &BigStruct{I: 7, J: 8, K: 9, L: 10, M: 11, N: 12, O: 13, P: 14, Q: 15, R: 16}) @@ -392,7 +456,7 @@ func main() { pad2: 200, } // Expected: - // all variables: globalInt globalStruct globalStructPtr s i err + // all variables: s i err // s.i8: '\x01' // s.i16: 2 // s.i32: 3 @@ -419,6 +483,8 @@ func main() { globalStructPtr = &s globalStruct = s println("globalInt:", globalInt) + // Expected(skip): + // all variables: globalInt globalStruct globalStructPtr s i err println("s:", &s) FuncWithAllTypeStructParam(s) println("called function with struct") @@ -437,14 +503,18 @@ func main() { s.fn, ) println(i, err) - println("called function with types") + ScopeIf(1) + ScopeIf(0) + ScopeFor() println(globalStructPtr) println(&globalStruct) s.i8 = 0x12 println(s.i8) // Expected: - // all variables: globalInt globalStruct globalStructPtr s i err + // all variables: s i err // s.i8: '\x12' + + // Expected(skip): // globalStruct.i8: '\x01' println((*globalStructPtr).i8) println("done") diff --git a/cl/compile.go b/cl/compile.go index 65e0c44b..42c2d2da 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -238,6 +238,9 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun sig = types.NewSignatureType(nil, nil, nil, params, results, false) } fn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), hasCtx, f.Origin() != nil) + if debugSymbols { + fn.Inline(llssa.NoInline) + } } if nblk := len(f.Blocks); nblk > 0 { @@ -292,18 +295,6 @@ func (p *context) compileFuncDecl(pkg llssa.Package, f *ssa.Function) (llssa.Fun return fn, nil, goFunc } -func (p *context) getDebugPosition(f *ssa.Function, pos token.Pos) token.Position { - ppos := p.goProg.Fset.Position(pos) - bodyPos := p.getFuncBodyPos(f) - if bodyPos.Filename == "" { - return ppos - } - if ppos.Line < bodyPos.Line { - return bodyPos - } - return ppos -} - func (p *context) getFuncBodyPos(f *ssa.Function) token.Position { if f.Object() != nil { return p.goProg.Fset.Position(f.Object().(*types.Func).Scope().Pos()) @@ -311,6 +302,11 @@ func (p *context) getFuncBodyPos(f *ssa.Function) token.Position { return p.goProg.Fset.Position(f.Pos()) } +func isGlobal(v *types.Var) bool { + // TODO(lijie): better implementation + return strings.HasPrefix(v.Parent().String(), "package ") +} + func (p *context) debugRef(b llssa.Builder, v *ssa.DebugRef) { object := v.Object() variable, ok := object.(*types.Var) @@ -322,15 +318,21 @@ func (p *context) debugRef(b llssa.Builder, v *ssa.DebugRef) { // skip *ssa.FieldAddr return } + if isGlobal(variable) { + // avoid generate local variable debug info of global variable in function + return + } pos := p.goProg.Fset.Position(v.Pos()) value := p.compileValue(b, v.X) fn := v.Parent() dbgVar := p.getLocalVariable(b, fn, variable) + scope := variable.Parent() + diScope := b.DIScope(p.fn, scope) if v.IsAddr { // *ssa.Alloc - b.DIDeclare(variable, value, dbgVar, p.fn, pos, b.Func.Block(v.Block().Index)) + b.DIDeclare(variable, value, dbgVar, diScope, pos, b.Func.Block(v.Block().Index)) } else { - b.DIValue(variable, value, dbgVar, p.fn, pos, b.Func.Block(v.Block().Index)) + b.DIValue(variable, value, dbgVar, diScope, pos, b.Func.Block(v.Block().Index)) } } @@ -527,10 +529,6 @@ func (p *context) compileInstrOrValue(b llssa.Builder, iv instrOrValue, asValue } log.Panicln("unreachable:", iv) } - if debugSymbols { - pos := p.getDebugPosition(iv.Parent(), iv.Pos()) - b.DISetCurrentDebugLocation(p.fn, pos) - } switch v := iv.(type) { case *ssa.Call: ret = p.call(b, llssa.Call, &v.Call) @@ -698,11 +696,27 @@ func (p *context) jumpTo(v *ssa.Jump) llssa.BasicBlock { return fn.Block(succs[0].Index) } +func (p *context) getDebugLocScope(v *ssa.Function, pos token.Pos) *types.Scope { + if v.Object() == nil { + return nil + } + funcScope := v.Object().(*types.Func).Scope() + return funcScope.Innermost(pos) +} + func (p *context) compileInstr(b llssa.Builder, instr ssa.Instruction) { if iv, ok := instr.(instrOrValue); ok { p.compileInstrOrValue(b, iv, false) return } + if debugSymbols { + scope := p.getDebugLocScope(instr.Parent(), instr.Pos()) + if scope != nil { + diScope := b.DIScope(p.fn, scope) + pos := p.fset.Position(instr.Pos()) + b.DISetCurrentDebugLocation(diScope, pos) + } + } switch v := instr.(type) { case *ssa.Store: va := v.Addr @@ -779,7 +793,8 @@ func (p *context) getLocalVariable(b llssa.Builder, fn *ssa.Function, v *types.V return b.DIVarParam(p.fn, pos, v.Name(), t, argNo) } } - return b.DIVarAuto(p.fn, pos, v.Name(), t) + scope := b.DIScope(p.fn, v.Parent()) + return b.DIVarAuto(scope, pos, v.Name(), t) } func (p *context) compileFunction(v *ssa.Function) (goFn llssa.Function, pyFn llssa.PyObjRef, kind int) { diff --git a/cl/instr.go b/cl/instr.go index 67d0e2c6..25520b2c 100644 --- a/cl/instr.go +++ b/cl/instr.go @@ -267,6 +267,9 @@ func (p *context) funcOf(fn *ssa.Function) (aFn llssa.Function, pyFn llssa.PyObj } sig := fn.Signature aFn = pkg.NewFuncEx(name, sig, llssa.Background(ftype), false, fn.Origin() != nil) + if debugSymbols { + aFn.Inline(llssa.NoInline) + } } } return diff --git a/ssa/decl.go b/ssa/decl.go index 13f944c7..0593923a 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -273,7 +273,8 @@ func (p Function) NewBuilder() Builder { b := prog.ctx.NewBuilder() // TODO(xsw): Finalize may cause panic, so comment it. // b.Finalize() - return &aBuilder{b, nil, p, p.Pkg, prog, make(map[Expr]dbgExpr)} + return &aBuilder{b, nil, p, p.Pkg, prog, + make(map[Expr]dbgExpr), make(map[*types.Scope]DIScope)} } // HasBody reports whether the function has a body. @@ -327,3 +328,23 @@ func (p Function) SetRecover(blk BasicBlock) { } // ----------------------------------------------------------------------------- + +type inlineAttr int + +const ( + NoInline inlineAttr = iota + AlwaysInline + InlineHint +) + +func (p Function) Inline(inline inlineAttr) { + inlineAttrName := map[inlineAttr]string{ + NoInline: "noinline", + AlwaysInline: "alwaysinline", + InlineHint: "inlinehint", + }[inline] + inlineAttr := p.Pkg.mod.Context().CreateEnumAttribute(llvm.AttributeKindID(inlineAttrName), 0) + p.impl.AddFunctionAttr(inlineAttr) +} + +// ----------------------------------------------------------------------------- diff --git a/ssa/di.go b/ssa/di.go index 93b42c89..fbd33300 100644 --- a/ssa/di.go +++ b/ssa/di.go @@ -6,6 +6,7 @@ import ( "go/token" "go/types" "path/filepath" + "unsafe" "github.com/goplus/llvm" ) @@ -19,6 +20,7 @@ type aDIBuilder struct { prog Program types map[Type]DIType positioner Positioner + m llvm.Module // Add this field } type diBuilder = *aDIBuilder @@ -26,20 +28,21 @@ type diBuilder = *aDIBuilder func newDIBuilder(prog Program, pkg Package, positioner Positioner) diBuilder { m := pkg.mod ctx := m.Context() - m.AddNamedMetadataOperand("llvm.module.flags", - ctx.MDNode([]llvm.Metadata{ - llvm.ConstInt(ctx.Int32Type(), 2, false).ConstantAsMetadata(), // Warning on mismatch - ctx.MDString("Debug Info Version"), - llvm.ConstInt(ctx.Int32Type(), 3, false).ConstantAsMetadata(), - }), - ) - m.AddNamedMetadataOperand("llvm.module.flags", - ctx.MDNode([]llvm.Metadata{ - llvm.ConstInt(ctx.Int32Type(), 7, false).ConstantAsMetadata(), // Max on mismatch - ctx.MDString("Dwarf Version"), - llvm.ConstInt(ctx.Int32Type(), 5, false).ConstantAsMetadata(), - }), - ) + + b := &aDIBuilder{ + di: llvm.NewDIBuilder(m), + prog: prog, + types: make(map[*aType]DIType), + positioner: positioner, + m: m, // Initialize the m field + } + + b.addNamedMetadataOperand("llvm.module.flags", 2, "Debug Info Version", 3) + b.addNamedMetadataOperand("llvm.module.flags", 7, "Dwarf Version", 5) + b.addNamedMetadataOperand("llvm.module.flags", 1, "wchar_size", 4) + b.addNamedMetadataOperand("llvm.module.flags", 8, "PIC Level", 2) + b.addNamedMetadataOperand("llvm.module.flags", 7, "uwtable", 1) + b.addNamedMetadataOperand("llvm.module.flags", 7, "frame-pointer", 1) // Add llvm.ident metadata identNode := ctx.MDNode([]llvm.Metadata{ @@ -47,12 +50,19 @@ func newDIBuilder(prog Program, pkg Package, positioner Positioner) diBuilder { }) m.AddNamedMetadataOperand("llvm.ident", identNode) - return &aDIBuilder{ - di: llvm.NewDIBuilder(m), - prog: prog, - types: make(map[*aType]DIType), - positioner: positioner, - } + return b +} + +// New method to add named metadata operand +func (b diBuilder) addNamedMetadataOperand(name string, intValue int, stringValue string, intValue2 int) { + ctx := b.m.Context() + b.m.AddNamedMetadataOperand(name, + ctx.MDNode([]llvm.Metadata{ + llvm.ConstInt(ctx.Int32Type(), uint64(intValue), false).ConstantAsMetadata(), + ctx.MDString(stringValue), + llvm.ConstInt(ctx.Int32Type(), uint64(intValue2), false).ConstantAsMetadata(), + }), + ) } // ---------------------------------------------------------------------------- @@ -225,16 +235,28 @@ func (b diBuilder) createGlobalVariableExpression(scope DIScope, pos token.Posit // ---------------------------------------------------------------------------- +type aDILexicalBlock struct { + ll llvm.Metadata +} + +type DILexicalBlock = *aDILexicalBlock + +func (l *aDILexicalBlock) scopeMeta(b diBuilder, pos token.Position) DIScopeMeta { + return &aDIScopeMeta{l.ll} +} + +// ---------------------------------------------------------------------------- + type aDIVar struct { ll llvm.Metadata } type DIVar = *aDIVar -func (b diBuilder) createParameterVariable(f Function, pos token.Position, name string, argNo int, ty DIType) DIVar { +func (b diBuilder) createParameterVariable(scope DIScope, pos token.Position, name string, argNo int, ty DIType) DIVar { return &aDIVar{ ll: b.di.CreateParameterVariable( - f.scopeMeta(b, pos).ll, + scope.scopeMeta(b, pos).ll, llvm.DIParameterVariable{ Name: name, File: b.file(pos.Filename).ll, @@ -513,9 +535,9 @@ func (b diBuilder) diTypeEx(name string, t Type, pos token.Position) DIType { return ty } -func (b diBuilder) varParam(f Function, pos token.Position, varName string, vt DIType, argNo int) DIVar { +func (b diBuilder) varParam(scope DIScope, pos token.Position, varName string, vt DIType, argNo int) DIVar { return b.createParameterVariable( - f, + scope, pos, varName, argNo, @@ -523,8 +545,8 @@ func (b diBuilder) varParam(f Function, pos token.Position, varName string, vt D ) } -func (b diBuilder) varAuto(f Function, pos token.Position, varName string, vt DIType) DIVar { - return b.createAutoVariable(f, pos, varName, vt) +func (b diBuilder) varAuto(scope DIScope, pos token.Position, varName string, vt DIType) DIVar { + return b.createAutoVariable(scope, pos, varName, vt) } func (b diBuilder) file(filename string) DIFile { @@ -546,21 +568,21 @@ func (b diBuilder) createExpression(ops []uint64) DIExpression { // ----------------------------------------------------------------------------- // Copy to alloca'd memory to get declareable address. -func (b Builder) constructDebugAddr(v Expr) (dbgPtr Expr, dbgVal Expr, deref bool) { +func (b Builder) constructDebugAddr(v Expr) (dbgPtr Expr, dbgVal Expr, exists bool) { if v, ok := b.dbgVars[v]; ok { - return v.ptr, v.val, v.deref + return v.ptr, v.val, true } t := v.Type.RawType().Underlying() - dbgPtr, dbgVal, deref = b.doConstructDebugAddr(v, t) - b.dbgVars[v] = dbgExpr{dbgPtr, dbgVal, deref} - return dbgPtr, dbgVal, deref + dbgPtr, dbgVal = b.doConstructDebugAddr(v, t) + dbgExpr := dbgExpr{dbgPtr, dbgVal} + b.dbgVars[v] = dbgExpr + b.dbgVars[dbgVal] = dbgExpr + return dbgPtr, dbgVal, false } -func (b Builder) doConstructDebugAddr(v Expr, t types.Type) (dbgPtr Expr, dbgVal Expr, deref bool) { +func (b Builder) doConstructDebugAddr(v Expr, t types.Type) (dbgPtr Expr, dbgVal Expr) { var ty Type switch t := t.(type) { - case *types.Pointer: - return v, v, false case *types.Basic: if t.Info()&types.IsComplex != 0 { if t.Kind() == types.Complex128 { @@ -590,7 +612,7 @@ func (b Builder) doConstructDebugAddr(v Expr, t types.Type) (dbgPtr Expr, dbgVal dbgPtr.Type = b.Prog.Pointer(v.Type) b.Store(dbgPtr, v) dbgVal = b.Load(dbgPtr) - return dbgPtr, dbgVal, deref + return dbgPtr, dbgVal } func (b Builder) di() diBuilder { @@ -598,13 +620,7 @@ func (b Builder) di() diBuilder { } func (b Builder) DIParam(variable *types.Var, v Expr, dv DIVar, scope DIScope, pos token.Position, blk BasicBlock) { - ty := v.Type.RawType().Underlying() - if isPtrType(ty) { - expr := b.di().createExpression(nil) - b.di().dbgDeclare(v, dv, scope, pos, expr, blk) - } else { - b.DIValue(variable, v, dv, scope, pos, blk) - } + b.DIValue(variable, v, dv, scope, pos, blk) } func (b Builder) DIDeclare(variable *types.Var, v Expr, dv DIVar, scope DIScope, pos token.Position, blk BasicBlock) { @@ -614,14 +630,13 @@ func (b Builder) DIDeclare(variable *types.Var, v Expr, dv DIVar, scope DIScope, func (b Builder) DIValue(variable *types.Var, v Expr, dv DIVar, scope DIScope, pos token.Position, blk BasicBlock) { ty := v.Type.RawType().Underlying() - if isPtrType(ty) { + if !needConstructAddr(ty) { expr := b.di().createExpression(nil) b.di().dbgValue(v, dv, scope, pos, expr, blk) } else { - dbgPtr, dbgVal, _ := b.constructDebugAddr(v) + dbgPtr, _, _ := b.constructDebugAddr(v) expr := b.di().createExpression([]uint64{opDeref}) b.di().dbgValue(dbgPtr, dv, scope, pos, expr, blk) - v.impl = dbgVal.impl } } @@ -629,25 +644,79 @@ const ( opDeref = 0x06 ) -func isPtrType(t types.Type) bool { - switch t.(type) { - case *types.Pointer: - return true - default: +func needConstructAddr(t types.Type) bool { + switch t := t.(type) { + case *types.Basic: + if t.Info()&types.IsComplex != 0 { + return true + } else if t.Info()&types.IsString != 0 { + return true + } return false + case *types.Pointer: + return false + default: + return true } } -func (b Builder) DIVarParam(f Function, pos token.Position, varName string, vt Type, argNo int) DIVar { +func (b Builder) DIVarParam(scope DIScope, pos token.Position, varName string, vt Type, argNo int) DIVar { t := b.di().diType(vt, pos) - return b.di().varParam(f, pos, varName, t, argNo) + return b.di().varParam(scope, pos, varName, t, argNo) } -func (b Builder) DIVarAuto(f Function, pos token.Position, varName string, vt Type) DIVar { +func (b Builder) DIVarAuto(scope DIScope, pos token.Position, varName string, vt Type) DIVar { t := b.di().diType(vt, pos) - return b.di().varAuto(f, pos, varName, t) + return b.di().varAuto(scope, pos, varName, t) } +// hack for types.Scope +type hackScope struct { + parent *types.Scope + children []*types.Scope + number int // parent.children[number-1] is this scope; 0 if there is no parent + elems map[string]types.Object // lazily allocated + pos, end token.Pos // scope extent; may be invalid + comment string // for debugging only + isFunc bool // set if this is a function scope (internal use only) +} + +func isFunc(scope *types.Scope) bool { + hs := (*hackScope)(unsafe.Pointer(scope)) + return hs.isFunc +} + +func (b Builder) DIScope(f Function, scope *types.Scope) DIScope { + if cachedScope, ok := b.diScopeCache[scope]; ok { + return cachedScope + } + pos := b.di().positioner.Position(scope.Pos()) + // skip package and universe scope + // if scope.Parent().Parent() == nil { + // return b.di().file(pos.Filename) + // } + + var result DIScope + if isFunc(scope) { + // TODO(lijie): should check scope == function scope + result = f + } else { + parentScope := b.DIScope(f, scope.Parent()) + result = &aDILexicalBlock{b.di().di.CreateLexicalBlock(parentScope.scopeMeta(b.di(), pos).ll, llvm.DILexicalBlock{ + File: b.di().file(pos.Filename).ll, + Line: pos.Line, + Column: pos.Column, + })} + } + + b.diScopeCache[scope] = result + return result +} + +const ( + MD_dbg = 0 +) + func (b Builder) DIGlobal(v Expr, name string, pos token.Position) { if _, ok := b.Pkg.glbDbgVars[v]; ok { return @@ -660,16 +729,16 @@ func (b Builder) DIGlobal(v Expr, name string, pos token.Position) { v.Type, false, ) - v.impl.AddMetadata(0, gv.ll) + v.impl.AddMetadata(MD_dbg, gv.ll) b.Pkg.glbDbgVars[v] = true } -func (b Builder) DISetCurrentDebugLocation(f Function, pos token.Position) { +func (b Builder) DISetCurrentDebugLocation(diScope DIScope, pos token.Position) { b.impl.SetCurrentDebugLocation( uint(pos.Line), uint(pos.Column), - f.scopeMeta(b.di(), pos).ll, - f.impl.InstructionDebugLoc(), + diScope.scopeMeta(b.di(), pos).ll, + llvm.Metadata{}, ) } @@ -695,6 +764,7 @@ func (b Builder) DebugFunction(f Function, pos token.Position, bodyPos token.Pos Line: pos.Line, ScopeLine: bodyPos.Line, IsDefinition: true, + LocalToUnit: true, Optimized: true, } p.diFunc = &aDIFunction{ diff --git a/ssa/stmt_builder.go b/ssa/stmt_builder.go index 042a5687..10128ce6 100644 --- a/ssa/stmt_builder.go +++ b/ssa/stmt_builder.go @@ -58,9 +58,8 @@ func (p BasicBlock) Addr() Expr { // ----------------------------------------------------------------------------- type dbgExpr struct { - ptr Expr - val Expr - deref bool + ptr Expr + val Expr } type aBuilder struct { @@ -70,7 +69,8 @@ type aBuilder struct { Pkg Package Prog Program - dbgVars map[Expr]dbgExpr + dbgVars map[Expr]dbgExpr // save copied address and values for debug info + diScopeCache map[*types.Scope]DIScope // avoid duplicated DILexicalBlock(s) } // Builder represents a builder for creating instructions in a function.