Skip to content

Commit c8b5042

Browse files
aykevldeadprogram
authored andcommitted
compiler: refactor creation of functions
1 parent ad992e2 commit c8b5042

File tree

2 files changed

+107
-97
lines changed

2 files changed

+107
-97
lines changed

compiler/calls.go

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,14 @@ func expandFormalParamType(t llvm.Type) []llvm.Type {
8282
}
8383
}
8484

85-
// Expand an argument type to a list of offsets from the start of the object.
86-
// Used together with expandFormalParam to get the offset of each value from the
87-
// start of the non-expanded value.
88-
func (c *Compiler) expandFormalParamOffsets(t llvm.Type) []uint64 {
85+
// expandFormalParamOffsets returns a list of offsets from the start of an
86+
// object of type t after it would have been split up by expandFormalParam. This
87+
// is useful for debug information, where it is necessary to know the offset
88+
// from the start of the combined object.
89+
func (b *builder) expandFormalParamOffsets(t llvm.Type) []uint64 {
8990
switch t.TypeKind() {
9091
case llvm.StructTypeKind:
91-
fields := c.flattenAggregateTypeOffsets(t)
92+
fields := b.flattenAggregateTypeOffsets(t)
9293
if len(fields) <= MaxFieldsPerParam {
9394
return fields
9495
} else {
@@ -162,10 +163,13 @@ func flattenAggregateType(t llvm.Type) []llvm.Type {
162163
}
163164
}
164165

165-
// Return the offsets from the start of the object if this object type were
166-
// flattened like in flattenAggregate. Used together with flattenAggregate to
167-
// know the start indices of each value in the non-flattened object.
168-
func (c *Compiler) flattenAggregateTypeOffsets(t llvm.Type) []uint64 {
166+
// flattenAggregateTypeOffset returns the offsets from the start of an object of
167+
// type t if this object were flattened like in flattenAggregate. Used together
168+
// with flattenAggregate to know the start indices of each value in the
169+
// non-flattened object.
170+
//
171+
// Note: this is an implementation detail, use expandFormalParamOffsets instead.
172+
func (c *compilerContext) flattenAggregateTypeOffsets(t llvm.Type) []uint64 {
169173
switch t.TypeKind() {
170174
case llvm.StructTypeKind:
171175
fields := make([]uint64, 0, t.StructElementTypesCount())
@@ -217,25 +221,28 @@ func (b *builder) flattenAggregate(v llvm.Value) []llvm.Value {
217221
}
218222
}
219223

220-
// Collapse a list of fields into its original value.
221-
func (c *Compiler) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Value {
222-
param, remaining := c.collapseFormalParamInternal(t, fields)
224+
// collapseFormalParam combines an aggregate object back into the original
225+
// value. This is used to join multiple LLVM parameters into a single Go value
226+
// in the function entry block.
227+
func (b *builder) collapseFormalParam(t llvm.Type, fields []llvm.Value) llvm.Value {
228+
param, remaining := b.collapseFormalParamInternal(t, fields)
223229
if len(remaining) != 0 {
224230
panic("failed to expand back all fields")
225231
}
226232
return param
227233
}
228234

229-
// Returns (value, remainingFields). Used by collapseFormalParam.
230-
func (c *Compiler) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) {
235+
// collapseFormalParamInternal is an implementation detail of
236+
// collapseFormalParam: it works by recursing until there are no fields left.
237+
func (b *builder) collapseFormalParamInternal(t llvm.Type, fields []llvm.Value) (llvm.Value, []llvm.Value) {
231238
switch t.TypeKind() {
232239
case llvm.StructTypeKind:
233240
if len(flattenAggregateType(t)) <= MaxFieldsPerParam {
234241
value := llvm.ConstNull(t)
235242
for i, subtyp := range t.StructElementTypes() {
236-
structField, remaining := c.collapseFormalParamInternal(subtyp, fields)
243+
structField, remaining := b.collapseFormalParamInternal(subtyp, fields)
237244
fields = remaining
238-
value = c.builder.CreateInsertValue(value, structField, i, "")
245+
value = b.CreateInsertValue(value, structField, i, "")
239246
}
240247
return value, fields
241248
} else {

compiler/compiler.go

Lines changed: 84 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ func (c *Compiler) Compile(mainPath string) []error {
297297
if frame.fn.Blocks == nil {
298298
continue // external function
299299
}
300-
c.parseFunc(frame)
300+
frame.createFunctionDefinition()
301301
}
302302

303303
// After all packages are imported, add a synthetic initializer function
@@ -689,40 +689,40 @@ func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
689689
// getLocalVariable returns a debug info entry for a local variable, which may
690690
// either be a parameter or a regular variable. It will create a new metadata
691691
// entry if there isn't one for the variable yet.
692-
func (c *Compiler) getLocalVariable(frame *Frame, variable *types.Var) llvm.Metadata {
693-
if dilocal, ok := frame.dilocals[variable]; ok {
692+
func (b *builder) getLocalVariable(variable *types.Var) llvm.Metadata {
693+
if dilocal, ok := b.dilocals[variable]; ok {
694694
// DILocalVariable was already created, return it directly.
695695
return dilocal
696696
}
697697

698-
pos := c.ir.Program.Fset.Position(variable.Pos())
698+
pos := b.ir.Program.Fset.Position(variable.Pos())
699699

700700
// Check whether this is a function parameter.
701-
for i, param := range frame.fn.Params {
701+
for i, param := range b.fn.Params {
702702
if param.Object().(*types.Var) == variable {
703703
// Yes it is, create it as a function parameter.
704-
dilocal := c.dibuilder.CreateParameterVariable(frame.difunc, llvm.DIParameterVariable{
704+
dilocal := b.dibuilder.CreateParameterVariable(b.difunc, llvm.DIParameterVariable{
705705
Name: param.Name(),
706-
File: c.getDIFile(pos.Filename),
706+
File: b.getDIFile(pos.Filename),
707707
Line: pos.Line,
708-
Type: c.getDIType(variable.Type()),
708+
Type: b.getDIType(variable.Type()),
709709
AlwaysPreserve: true,
710710
ArgNo: i + 1,
711711
})
712-
frame.dilocals[variable] = dilocal
712+
b.dilocals[variable] = dilocal
713713
return dilocal
714714
}
715715
}
716716

717717
// No, it's not a parameter. Create a regular (auto) variable.
718-
dilocal := c.dibuilder.CreateAutoVariable(frame.difunc, llvm.DIAutoVariable{
718+
dilocal := b.dibuilder.CreateAutoVariable(b.difunc, llvm.DIAutoVariable{
719719
Name: variable.Name(),
720-
File: c.getDIFile(pos.Filename),
720+
File: b.getDIFile(pos.Filename),
721721
Line: pos.Line,
722-
Type: c.getDIType(variable.Type()),
722+
Type: b.getDIType(variable.Type()),
723723
AlwaysPreserve: true,
724724
})
725-
frame.dilocals[variable] = dilocal
725+
b.dilocals[variable] = dilocal
726726
return dilocal
727727
}
728728

@@ -845,86 +845,89 @@ func (c *compilerContext) getDIFile(filename string) llvm.Metadata {
845845
return c.difiles[filename]
846846
}
847847

848-
func (c *Compiler) parseFunc(frame *Frame) {
849-
if c.DumpSSA() {
850-
fmt.Printf("\nfunc %s:\n", frame.fn.Function)
848+
// createFunctionDefinition builds the LLVM IR implementation for this function.
849+
// The function must be declared but not yet defined, otherwise this function
850+
// will create a diagnostic.
851+
func (b *builder) createFunctionDefinition() {
852+
if b.DumpSSA() {
853+
fmt.Printf("\nfunc %s:\n", b.fn.Function)
851854
}
852-
if !frame.fn.LLVMFn.IsDeclaration() {
853-
errValue := frame.fn.LLVMFn.Name() + " redeclared in this program"
854-
fnPos := getPosition(frame.fn.LLVMFn)
855+
if !b.fn.LLVMFn.IsDeclaration() {
856+
errValue := b.fn.Name() + " redeclared in this program"
857+
fnPos := getPosition(b.fn.LLVMFn)
855858
if fnPos.IsValid() {
856859
errValue += "\n\tprevious declaration at " + fnPos.String()
857860
}
858-
c.addError(frame.fn.Pos(), errValue)
861+
b.addError(b.fn.Pos(), errValue)
859862
return
860863
}
861-
if !frame.fn.IsExported() {
862-
frame.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
863-
frame.fn.LLVMFn.SetUnnamedAddr(true)
864+
if !b.fn.IsExported() {
865+
b.fn.LLVMFn.SetLinkage(llvm.InternalLinkage)
866+
b.fn.LLVMFn.SetUnnamedAddr(true)
864867
}
865868

866869
// Some functions have a pragma controlling the inlining level.
867-
switch frame.fn.Inline() {
870+
switch b.fn.Inline() {
868871
case ir.InlineHint:
869872
// Add LLVM inline hint to functions with //go:inline pragma.
870-
inline := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("inlinehint"), 0)
871-
frame.fn.LLVMFn.AddFunctionAttr(inline)
873+
inline := b.ctx.CreateEnumAttribute(llvm.AttributeKindID("inlinehint"), 0)
874+
b.fn.LLVMFn.AddFunctionAttr(inline)
872875
case ir.InlineNone:
873876
// Add LLVM attribute to always avoid inlining this function.
874-
noinline := c.ctx.CreateEnumAttribute(llvm.AttributeKindID("noinline"), 0)
875-
frame.fn.LLVMFn.AddFunctionAttr(noinline)
877+
noinline := b.ctx.CreateEnumAttribute(llvm.AttributeKindID("noinline"), 0)
878+
b.fn.LLVMFn.AddFunctionAttr(noinline)
876879
}
877880

878881
// Add debug info, if needed.
879-
if c.Debug() {
880-
if frame.fn.Synthetic == "package initializer" {
882+
if b.Debug() {
883+
if b.fn.Synthetic == "package initializer" {
881884
// Package initializers have no debug info. Create some fake debug
882885
// info to at least have *something*.
883-
frame.difunc = c.attachDebugInfoRaw(frame.fn, frame.fn.LLVMFn, "", "", 0)
884-
} else if frame.fn.Syntax() != nil {
886+
b.difunc = b.attachDebugInfoRaw(b.fn, b.fn.LLVMFn, "", "", 0)
887+
} else if b.fn.Syntax() != nil {
885888
// Create debug info file if needed.
886-
frame.difunc = c.attachDebugInfo(frame.fn)
889+
b.difunc = b.attachDebugInfo(b.fn)
887890
}
888-
pos := c.ir.Program.Fset.Position(frame.fn.Pos())
889-
c.builder.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), frame.difunc, llvm.Metadata{})
891+
pos := b.ir.Program.Fset.Position(b.fn.Pos())
892+
b.SetCurrentDebugLocation(uint(pos.Line), uint(pos.Column), b.difunc, llvm.Metadata{})
890893
}
891894

892895
// Pre-create all basic blocks in the function.
893-
for _, block := range frame.fn.DomPreorder() {
894-
llvmBlock := c.ctx.AddBasicBlock(frame.fn.LLVMFn, block.Comment)
895-
frame.blockEntries[block] = llvmBlock
896-
frame.blockExits[block] = llvmBlock
896+
for _, block := range b.fn.DomPreorder() {
897+
llvmBlock := b.ctx.AddBasicBlock(b.fn.LLVMFn, block.Comment)
898+
b.blockEntries[block] = llvmBlock
899+
b.blockExits[block] = llvmBlock
897900
}
898-
entryBlock := frame.blockEntries[frame.fn.Blocks[0]]
899-
c.builder.SetInsertPointAtEnd(entryBlock)
901+
entryBlock := b.blockEntries[b.fn.Blocks[0]]
902+
b.SetInsertPointAtEnd(entryBlock)
900903

901904
// Load function parameters
902905
llvmParamIndex := 0
903-
for _, param := range frame.fn.Params {
904-
llvmType := c.getLLVMType(param.Type())
906+
for _, param := range b.fn.Params {
907+
llvmType := b.getLLVMType(param.Type())
905908
fields := make([]llvm.Value, 0, 1)
906909
for range expandFormalParamType(llvmType) {
907-
fields = append(fields, frame.fn.LLVMFn.Param(llvmParamIndex))
910+
fields = append(fields, b.fn.LLVMFn.Param(llvmParamIndex))
908911
llvmParamIndex++
909912
}
910-
frame.locals[param] = c.collapseFormalParam(llvmType, fields)
913+
b.locals[param] = b.collapseFormalParam(llvmType, fields)
911914

912915
// Add debug information to this parameter (if available)
913-
if c.Debug() && frame.fn.Syntax() != nil {
914-
dbgParam := c.getLocalVariable(frame, param.Object().(*types.Var))
915-
loc := c.builder.GetCurrentDebugLocation()
916+
if b.Debug() && b.fn.Syntax() != nil {
917+
dbgParam := b.getLocalVariable(param.Object().(*types.Var))
918+
loc := b.GetCurrentDebugLocation()
916919
if len(fields) == 1 {
917-
expr := c.dibuilder.CreateExpression(nil)
918-
c.dibuilder.InsertValueAtEnd(fields[0], dbgParam, expr, loc, entryBlock)
920+
expr := b.dibuilder.CreateExpression(nil)
921+
b.dibuilder.InsertValueAtEnd(fields[0], dbgParam, expr, loc, entryBlock)
919922
} else {
920-
fieldOffsets := c.expandFormalParamOffsets(llvmType)
923+
fieldOffsets := b.expandFormalParamOffsets(llvmType)
921924
for i, field := range fields {
922-
expr := c.dibuilder.CreateExpression([]int64{
925+
expr := b.dibuilder.CreateExpression([]int64{
923926
0x1000, // DW_OP_LLVM_fragment
924927
int64(fieldOffsets[i]) * 8, // offset in bits
925-
int64(c.targetData.TypeAllocSize(field.Type())) * 8, // size in bits
928+
int64(b.targetData.TypeAllocSize(field.Type())) * 8, // size in bits
926929
})
927-
c.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock)
930+
b.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock)
928931
}
929932
}
930933
}
@@ -933,44 +936,44 @@ func (c *Compiler) parseFunc(frame *Frame) {
933936
// Load free variables from the context. This is a closure (or bound
934937
// method).
935938
var context llvm.Value
936-
if !frame.fn.IsExported() {
937-
parentHandle := frame.fn.LLVMFn.LastParam()
939+
if !b.fn.IsExported() {
940+
parentHandle := b.fn.LLVMFn.LastParam()
938941
parentHandle.SetName("parentHandle")
939942
context = llvm.PrevParam(parentHandle)
940943
context.SetName("context")
941944
}
942-
if len(frame.fn.FreeVars) != 0 {
945+
if len(b.fn.FreeVars) != 0 {
943946
// Get a list of all variable types in the context.
944-
freeVarTypes := make([]llvm.Type, len(frame.fn.FreeVars))
945-
for i, freeVar := range frame.fn.FreeVars {
946-
freeVarTypes[i] = c.getLLVMType(freeVar.Type())
947+
freeVarTypes := make([]llvm.Type, len(b.fn.FreeVars))
948+
for i, freeVar := range b.fn.FreeVars {
949+
freeVarTypes[i] = b.getLLVMType(freeVar.Type())
947950
}
948951

949952
// Load each free variable from the context pointer.
950953
// A free variable is always a pointer when this is a closure, but it
951954
// can be another type when it is a wrapper for a bound method (these
952955
// wrappers are generated by the ssa package).
953-
for i, val := range c.emitPointerUnpack(context, freeVarTypes) {
954-
frame.locals[frame.fn.FreeVars[i]] = val
956+
for i, val := range b.emitPointerUnpack(context, freeVarTypes) {
957+
b.locals[b.fn.FreeVars[i]] = val
955958
}
956959
}
957960

958-
if frame.fn.Recover != nil {
961+
if b.fn.Recover != nil {
959962
// This function has deferred function calls. Set some things up for
960963
// them.
961-
frame.deferInitFunc()
964+
b.deferInitFunc()
962965
}
963966

964967
// Fill blocks with instructions.
965-
for _, block := range frame.fn.DomPreorder() {
966-
if c.DumpSSA() {
968+
for _, block := range b.fn.DomPreorder() {
969+
if b.DumpSSA() {
967970
fmt.Printf("%d: %s:\n", block.Index, block.Comment)
968971
}
969-
c.builder.SetInsertPointAtEnd(frame.blockEntries[block])
970-
frame.currentBlock = block
972+
b.SetInsertPointAtEnd(b.blockEntries[block])
973+
b.currentBlock = block
971974
for _, instr := range block.Instrs {
972975
if instr, ok := instr.(*ssa.DebugRef); ok {
973-
if !c.Debug() {
976+
if !b.Debug() {
974977
continue
975978
}
976979
object := instr.Object()
@@ -984,35 +987,35 @@ func (c *Compiler) parseFunc(frame *Frame) {
984987
// for example.
985988
continue
986989
}
987-
dbgVar := c.getLocalVariable(frame, variable)
988-
pos := c.ir.Program.Fset.Position(instr.Pos())
989-
c.dibuilder.InsertValueAtEnd(frame.getValue(instr.X), dbgVar, c.dibuilder.CreateExpression(nil), llvm.DebugLoc{
990+
dbgVar := b.getLocalVariable(variable)
991+
pos := b.ir.Program.Fset.Position(instr.Pos())
992+
b.dibuilder.InsertValueAtEnd(b.getValue(instr.X), dbgVar, b.dibuilder.CreateExpression(nil), llvm.DebugLoc{
990993
Line: uint(pos.Line),
991994
Col: uint(pos.Column),
992-
Scope: frame.difunc,
993-
}, c.builder.GetInsertBlock())
995+
Scope: b.difunc,
996+
}, b.GetInsertBlock())
994997
continue
995998
}
996-
if c.DumpSSA() {
999+
if b.DumpSSA() {
9971000
if val, ok := instr.(ssa.Value); ok && val.Name() != "" {
9981001
fmt.Printf("\t%s = %s\n", val.Name(), val.String())
9991002
} else {
10001003
fmt.Printf("\t%s\n", instr.String())
10011004
}
10021005
}
1003-
frame.createInstruction(instr)
1006+
b.createInstruction(instr)
10041007
}
1005-
if frame.fn.Name() == "init" && len(block.Instrs) == 0 {
1006-
c.builder.CreateRetVoid()
1008+
if b.fn.Name() == "init" && len(block.Instrs) == 0 {
1009+
b.CreateRetVoid()
10071010
}
10081011
}
10091012

10101013
// Resolve phi nodes
1011-
for _, phi := range frame.phis {
1014+
for _, phi := range b.phis {
10121015
block := phi.ssa.Block()
10131016
for i, edge := range phi.ssa.Edges {
1014-
llvmVal := frame.getValue(edge)
1015-
llvmBlock := frame.blockExits[block.Preds[i]]
1017+
llvmVal := b.getValue(edge)
1018+
llvmBlock := b.blockExits[block.Preds[i]]
10161019
phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock})
10171020
}
10181021
}

0 commit comments

Comments
 (0)