Skip to content
This repository has been archived by the owner on Dec 14, 2023. It is now read-only.

Commit

Permalink
Merge pull request #201 from pcc/libgo-lto1
Browse files Browse the repository at this point in the history
Enable more optimizations, and begin on LTO
  • Loading branch information
pcc committed Jul 8, 2014
2 parents f9c9745 + 5a57dc3 commit 5f5c7af
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 60 deletions.
129 changes: 104 additions & 25 deletions cmd/gllgo/gllgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ type driverOptions struct {
bprefix string
debugPrefixMaps []debug.PrefixMap
dumpSSA bool
emitIR bool
gccgoPath string
generateDebug bool
importPaths []string
Expand Down Expand Up @@ -214,7 +215,10 @@ func parseArguments(args []string) (opts driverOptions, err error) {
case args[0] == "-fno-toplevel-reorder":
// This is a GCC-specific code generation option. Ignore.

case args[0] == "-emit-llvm", args[0] == "-flto":
case args[0] == "-emit-llvm":
opts.emitIR = true

case args[0] == "-flto":
opts.lto = true

case args[0] == "-fPIC":
Expand Down Expand Up @@ -308,7 +312,7 @@ func parseArguments(args []string) (opts driverOptions, err error) {
return opts, nil
}

func runPasses(opts *driverOptions, m llvm.Module) {
func runPasses(opts *driverOptions, tm llvm.TargetMachine, m llvm.Module) {
fpm := llvm.NewFunctionPassManagerForModule(m)
defer fpm.Dispose()

Expand All @@ -321,6 +325,15 @@ func runPasses(opts *driverOptions, m llvm.Module) {
pmb.SetOptLevel(opts.optLevel)
pmb.SetSizeLevel(opts.sizeLevel)

target := tm.TargetData()
mpm.Add(target)
fpm.Add(target)
tm.AddAnalysisPasses(mpm)
tm.AddAnalysisPasses(fpm)

mpm.AddVerifierPass()
fpm.AddVerifierPass()

pmb.Populate(mpm)
pmb.PopulateFunc(fpm)

Expand All @@ -333,6 +346,41 @@ func runPasses(opts *driverOptions, m llvm.Module) {
mpm.Run(m)
}

func getMetadataSectionInlineAsm(name string) string {
// ELF: creates a non-allocated excluded section.
return ".section \"" + name + "\", \"e\"\n"
}

func getDataInlineAsm(data []byte) string {
edata := make([]byte, len(data)*4+10)

j := copy(edata, ".ascii \"")
for i := range data {
switch data[i] {
case '\000':
edata[j] = '\\'
edata[j+1] = '0'
edata[j+2] = '0'
edata[j+3] = '0'
j += 4
continue
case '\n':
edata[j] = '\\'
edata[j+1] = 'n'
j += 2
continue
case '"', '\\':
edata[j] = '\\'
j++
}
edata[j] = data[i]
j++
}
edata[j] = '"'
edata[j+1] = '\n'
return string(edata[0 : j+2])
}

func performAction(opts *driverOptions, kind actionKind, inputs []string, output string) error {
switch kind {
case actionPrint:
Expand Down Expand Up @@ -366,15 +414,28 @@ func performAction(opts *driverOptions, kind actionKind, inputs []string, output

defer module.Dispose()

// TODO(pcc): Decide how we want to expose export data for LTO.
if !opts.lto && module.ExportData != nil {
edatainit := llvm.ConstString(string(module.ExportData), false)
edataglobal := llvm.AddGlobal(module.Module, edatainit.Type(), module.Path+".export")
edataglobal.SetInitializer(edatainit)
edataglobal.SetSection(".go_export")
target, err := llvm.GetTargetFromTriple(opts.triple)
if err != nil {
return err
}

optLevel := [...]llvm.CodeGenOptLevel{
llvm.CodeGenLevelNone,
llvm.CodeGenLevelLess,
llvm.CodeGenLevelDefault,
llvm.CodeGenLevelAggressive,
}[opts.optLevel]

relocMode := llvm.RelocStatic
if opts.pic {
relocMode = llvm.RelocPIC
}

runPasses(opts, module.Module)
tm := target.CreateTargetMachine(opts.triple, "", "", optLevel,
relocMode, llvm.CodeModelDefault)
defer tm.Dispose()

runPasses(opts, tm, module.Module)

var file *os.File
if output == "-" {
Expand All @@ -388,33 +449,51 @@ func performAction(opts *driverOptions, kind actionKind, inputs []string, output
}

switch {
case !opts.lto:
target, err := llvm.GetTargetFromTriple(opts.triple)
case !opts.lto && !opts.emitIR:
if module.ExportData != nil {
asm := getMetadataSectionInlineAsm(".go_export")
asm += getDataInlineAsm(module.ExportData)
module.Module.SetInlineAsm(asm)
}

fileType := llvm.AssemblyFile
if kind == actionCompile {
fileType = llvm.ObjectFile
}
mb, err := tm.EmitToMemoryBuffer(module.Module, fileType)
if err != nil {
return err
}
defer mb.Dispose()

optLevel := [...]llvm.CodeGenOptLevel{
llvm.CodeGenLevelNone,
llvm.CodeGenLevelLess,
llvm.CodeGenLevelDefault,
llvm.CodeGenLevelAggressive,
}[opts.optLevel]
bytes := mb.Bytes()
_, err = file.Write(bytes)
return err

relocMode := llvm.RelocStatic
if opts.pic {
relocMode = llvm.RelocPIC
case opts.lto:
bcmb := llvm.WriteBitcodeToMemoryBuffer(module.Module)
defer bcmb.Dispose()

// This is a bit of a hack. We just want an object file
// containing some metadata sections. This might be simpler
// if we had bindings for the MC library, but for now we create
// a fresh module containing only inline asm that creates the
// sections.
outmodule := llvm.NewModule("")
defer outmodule.Dispose()
asm := getMetadataSectionInlineAsm(".llvmbc")
asm += getDataInlineAsm(bcmb.Bytes())
if module.ExportData != nil {
asm += getMetadataSectionInlineAsm(".go_export")
asm += getDataInlineAsm(module.ExportData)
}

tm := target.CreateTargetMachine(opts.triple, "", "", optLevel,
relocMode, llvm.CodeModelDefault)
defer tm.Dispose()
outmodule.SetInlineAsm(asm)

fileType := llvm.AssemblyFile
if kind == actionCompile {
fileType = llvm.ObjectFile
}
mb, err := tm.EmitToMemoryBuffer(module.Module, fileType)
mb, err := tm.EmitToMemoryBuffer(outmodule, fileType)
if err != nil {
return err
}
Expand Down
13 changes: 3 additions & 10 deletions interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,24 +69,17 @@ func (fr *frame) compareInterfaces(a, b *govalue) *govalue {
}

func (fr *frame) makeInterface(llv llvm.Value, vty types.Type, iface types.Type) *govalue {
i8ptr := llvm.PointerType(llvm.Int8Type(), 0)
if _, ok := vty.Underlying().(*types.Pointer); !ok {
ptr := fr.createTypeMalloc(vty)
fr.builder.CreateStore(llv, ptr)
llv = fr.builder.CreateBitCast(ptr, i8ptr, "")
llv = ptr
}
value := llvm.Undef(fr.types.ToLLVM(iface))
itab := fr.types.getItabPointer(vty, iface.Underlying().(*types.Interface))
value = fr.builder.CreateInsertValue(value, itab, 0, "")
value = fr.builder.CreateInsertValue(value, llv, 1, "")
return newValue(value, iface)
return fr.makeInterfaceFromPointer(llv, vty, iface)
}

func (fr *frame) makeInterfaceFromPointer(vptr llvm.Value, vty types.Type, iface types.Type) *govalue {
i8ptr := llvm.PointerType(llvm.Int8Type(), 0)
ptr := fr.createTypeMalloc(vty)
fr.memcpy(ptr, vptr, llvm.ConstInt(fr.types.inttype, uint64(fr.types.Sizeof(vty)), false))
llv := fr.builder.CreateBitCast(ptr, i8ptr, "")
llv := fr.builder.CreateBitCast(vptr, i8ptr, "")
value := llvm.Undef(fr.types.ToLLVM(iface))
itab := fr.types.getItabPointer(vty, iface.Underlying().(*types.Interface))
value = fr.builder.CreateInsertValue(value, itab, 0, "")
Expand Down
4 changes: 4 additions & 0 deletions maps.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ func (fr *frame) mapLookup(m, k *govalue) (v *govalue, ok *govalue) {
pk := fr.allocaBuilder.CreateAlloca(llk.Type(), "")
fr.builder.CreateStore(llk, pk)
valptr := fr.runtime.mapIndex.call(fr, m.value, pk, boolLLVMValue(false))[0]
valptr.AddInstrAttribute(2, llvm.NoCaptureAttribute)
valptr.AddInstrAttribute(2, llvm.ReadOnlyAttribute)
okbit := fr.builder.CreateIsNotNull(valptr, "")

elemtyp := m.Type().Underlying().(*types.Map).Elem()
Expand All @@ -44,6 +46,8 @@ func (fr *frame) mapUpdate(m, k, v *govalue) {
pk := fr.allocaBuilder.CreateAlloca(llk.Type(), "")
fr.builder.CreateStore(llk, pk)
valptr := fr.runtime.mapIndex.call(fr, m.value, pk, boolLLVMValue(true))[0]
valptr.AddInstrAttribute(2, llvm.NoCaptureAttribute)
valptr.AddInstrAttribute(2, llvm.ReadOnlyAttribute)

elemtyp := m.Type().Underlying().(*types.Map).Elem()
llelemtyp := fr.types.ToLLVM(elemtyp)
Expand Down
92 changes: 67 additions & 25 deletions ssa.go
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ type frame struct {
unwindBlock llvm.BasicBlock
frameptr llvm.Value
env map[ssa.Value]*govalue
ptr map[ssa.Value]llvm.Value
tuples map[ssa.Value][]*govalue
phis []pendingPhi
canRecover llvm.Value
Expand All @@ -464,6 +465,7 @@ func newFrame(u *unit, fn llvm.Value) *frame {
builder: llvm.GlobalContext().NewBuilder(),
allocaBuilder: llvm.GlobalContext().NewBuilder(),
env: make(map[ssa.Value]*govalue),
ptr: make(map[ssa.Value]llvm.Value),
tuples: make(map[ssa.Value][]*govalue),
}
}
Expand Down Expand Up @@ -685,6 +687,19 @@ func (fr *frame) nilCheck(v ssa.Value, llptr llvm.Value) {
}
}

func (fr *frame) canAvoidElementLoad(refs []ssa.Instruction) bool {
for _, ref := range refs {
switch ref.(type) {
case *ssa.Field, *ssa.Index:
// ok
default:
return false
}
}

return true
}

// If this value is sufficiently large, look through referrers to see if we can
// avoid a load.
func (fr *frame) canAvoidLoad(instr *ssa.UnOp, op llvm.Value) bool {
Expand All @@ -693,25 +708,32 @@ func (fr *frame) canAvoidLoad(instr *ssa.UnOp, op llvm.Value) bool {
return false
}

// We only know how to avoid loads if they are used to create an interface.
// If we see a non-MakeInterface referrer, abort.
// Keep track of whether our pointer may escape. We conservatively assume
// that MakeInterfaces will escape.
esc := false

// We only know how to avoid loads if they are used to create an interface
// or read an element of the structure. If we see any other referrer, abort.
for _, ref := range *instr.Referrers() {
if _, ok := ref.(*ssa.MakeInterface); !ok {
switch ref.(type) {
case *ssa.MakeInterface:
esc = true
case *ssa.Field, *ssa.Index:
// ok
default:
return false
}
}

// We now know that each referrer is a MakeInterface. Create the
// interfaces and store them in fr.env. Although the MakeInterface could
// be in another basic block, it is safe to create the interfaces here
// because the operation cannot fail. We cannot create the interface at
// the point where the MakeInterface instruction is because the memory
// might have changed in the meantime.
for _, ref := range *instr.Referrers() {
mi := ref.(*ssa.MakeInterface)
fr.env[mi] = fr.makeInterfaceFromPointer(op, instr.Type(), mi.Type())
var opcopy llvm.Value
if esc {
opcopy = fr.createTypeMalloc(instr.Type())
} else {
opcopy = fr.allocaBuilder.CreateAlloca(fr.types.ToLLVM(instr.Type()), "")
}
fr.memcpy(opcopy, op, llvm.ConstInt(fr.types.inttype, uint64(fr.types.Sizeof(instr.Type())), false))

fr.ptr[instr] = opcopy
return true
}

Expand Down Expand Up @@ -861,10 +883,19 @@ func (fr *frame) instruction(instr ssa.Instruction) {
fr.env[instr] = newValue(elem, elemtyp)

case *ssa.Field:
value := fr.llvmvalue(instr.X)
field := fr.builder.CreateExtractValue(value, instr.Field, instr.Name())
fieldtyp := instr.Type()
fr.env[instr] = newValue(field, fieldtyp)
if p, ok := fr.ptr[instr.X]; ok {
field := fr.builder.CreateStructGEP(p, instr.Field, instr.Name())
if fr.canAvoidElementLoad(*instr.Referrers()) {
fr.ptr[instr] = field
} else {
fr.env[instr] = newValue(fr.builder.CreateLoad(field, ""), fieldtyp)
}
} else {
value := fr.llvmvalue(instr.X)
field := fr.builder.CreateExtractValue(value, instr.Field, instr.Name())
fr.env[instr] = newValue(field, fieldtyp)
}

case *ssa.FieldAddr:
ptr := fr.llvmvalue(instr.X)
Expand All @@ -890,16 +921,21 @@ func (fr *frame) instruction(instr ssa.Instruction) {
fr.builder.CreateCondBr(cond, trueBlock, falseBlock)

case *ssa.Index:
// The optimiser will remove the alloca/store/load
// instructions if the array is already addressable.
array := fr.llvmvalue(instr.X)
arrayptr := fr.allocaBuilder.CreateAlloca(array.Type(), "")
arraytyp := instr.X.Type().Underlying().(*types.Array)
arraylen := llvm.ConstInt(fr.llvmtypes.inttype, uint64(arraytyp.Len()), false)
var arrayptr llvm.Value

if ptr, ok := fr.ptr[instr.X]; ok {
arrayptr = ptr
} else {
array := fr.llvmvalue(instr.X)
arrayptr = fr.allocaBuilder.CreateAlloca(array.Type(), "")

fr.builder.CreateStore(array, arrayptr)
fr.builder.CreateStore(array, arrayptr)
}
index := fr.llvmvalue(instr.Index)

arraytyp := instr.X.Type().Underlying().(*types.Array)
arraylen := llvm.ConstInt(fr.llvmtypes.inttype, uint64(arraytyp.Len()), false)

// The index may not have been promoted to int (for example, if it
// came from a composite literal).
index = fr.createZExtOrTrunc(index, fr.types.inttype, "")
Expand All @@ -914,7 +950,11 @@ func (fr *frame) instruction(instr ssa.Instruction) {
fr.condBrRuntimeError(cond, gccgoRuntimeErrorARRAY_INDEX_OUT_OF_BOUNDS)

addr := fr.builder.CreateGEP(arrayptr, []llvm.Value{zero, index}, "")
fr.env[instr] = newValue(fr.builder.CreateLoad(addr, ""), instr.Type())
if fr.canAvoidElementLoad(*instr.Referrers()) {
fr.ptr[instr] = addr
} else {
fr.env[instr] = newValue(fr.builder.CreateLoad(addr, ""), instr.Type())
}

case *ssa.IndexAddr:
x := fr.llvmvalue(instr.X)
Expand Down Expand Up @@ -988,8 +1028,10 @@ func (fr *frame) instruction(instr ssa.Instruction) {
fr.env[instr] = fr.makeClosure(fn, bindings)

case *ssa.MakeInterface:
// fr.env[instr] will be set if a pointer load was elided by canAvoidLoad
if _, ok := fr.env[instr]; !ok {
// fr.ptr[instr.X] will be set if a pointer load was elided by canAvoidLoad
if ptr, ok := fr.ptr[instr.X]; ok {
fr.env[instr] = fr.makeInterfaceFromPointer(ptr, instr.X.Type(), instr.Type())
} else {
receiver := fr.llvmvalue(instr.X)
fr.env[instr] = fr.makeInterface(receiver, instr.X.Type(), instr.Type())
}
Expand Down

0 comments on commit 5f5c7af

Please sign in to comment.