From 3887e0d2e7dbec751de9d56ddbc7ff5f9b438d2d Mon Sep 17 00:00:00 2001 From: Robin Eklind Date: Sat, 21 Dec 2019 05:42:16 +0100 Subject: [PATCH 01/11] irutil: add instruction and terminator operand use-tracking and value update API Rather than adding the use-tracking to llir/llvm/ir, we currently add this to irutil so we may get more experience with the API and see if its the right fit for instruction and terminator operand use-tracking and value update. Note: this is the stronger version of the API, as outlined by @pwaller in https://github.com/llir/llvm/issues/42#issue-383892282 Notably, operands are tracked by pointer (*value.Value) which also enables updating values using the same API. Fixes llir/llvm#42. --- go.mod | 2 + go.sum | 4 +- use.go | 458 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 462 insertions(+), 2 deletions(-) create mode 100644 use.go diff --git a/go.mod b/go.mod index 1240af6..3a28439 100644 --- a/go.mod +++ b/go.mod @@ -3,3 +3,5 @@ module github.com/llir/irutil go 1.12 require github.com/llir/llvm v0.3.0-pre7.0.20191220191840-fc6d0e8d9bc6 + +replace github.com/llir/llvm => github.com/mewpull/llvm v0.3.0-pre7.0.20191221041541-3f58af5a0014 diff --git a/go.sum b/go.sum index 8bee212..8912cd7 100644 --- a/go.sum +++ b/go.sum @@ -7,12 +7,12 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/llir/ll v0.0.0-20191205054844-50f4b743547b h1:Hc7BLGoz4hbN+8mb+JhZeYS+5mE8l+L+KWp7cJVeNrY= github.com/llir/ll v0.0.0-20191205054844-50f4b743547b/go.mod h1:8W5HJz80PitAyPZUpOcljQxTu6LD5YKW1URTo+OjVoc= -github.com/llir/llvm v0.3.0-pre7.0.20191220191840-fc6d0e8d9bc6 h1:bwZWin1Q/mernGFKoNOKRysPU10+rgylUxLeJR5AOJg= -github.com/llir/llvm v0.3.0-pre7.0.20191220191840-fc6d0e8d9bc6/go.mod h1:w6wEy+jHlAsmT+yQGYOvL1TO+JEDlzt+NnsYegCunuo= github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2 h1:EyTNMdePWaoWsRSGQnXiSoQu0r6RS1eA557AwJhlzHU= github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2/go.mod h1:3E2FUC/qYUfM8+r9zAwpeHJzqRVVMIYnpzD/clwWxyA= github.com/mewmew/float v0.0.0-20191218075745-e26b3d092977 h1:6+YqyNydbRNcd0azoFfSM36F1O5nbc0M3OzKqsT8YCA= github.com/mewmew/float v0.0.0-20191218075745-e26b3d092977/go.mod h1:O+xb+8ycBNHzJicFVs7GRWtruD4tVZI0huVnw5TM01E= +github.com/mewpull/llvm v0.3.0-pre7.0.20191221041541-3f58af5a0014 h1:STFmB7NoMfxXapksFUH+EwOTcruUQ+jaeFxZU+KiXL4= +github.com/mewpull/llvm v0.3.0-pre7.0.20191221041541-3f58af5a0014/go.mod h1:w6wEy+jHlAsmT+yQGYOvL1TO+JEDlzt+NnsYegCunuo= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/use.go b/use.go new file mode 100644 index 0000000..b8b8c23 --- /dev/null +++ b/use.go @@ -0,0 +1,458 @@ +package irutil + +import ( + "fmt" + + "github.com/llir/llvm/ir" + "github.com/llir/llvm/ir/value" +) + +// User is a user of a value. +// +// A User has one of the following underlying types. +// +// ir.Instruction // https://godoc.org/github.com/llir/llvm/ir#Instruction +// ir.Terminator // https://godoc.org/github.com/llir/llvm/ir#Terminator +type User interface { + ir.LLStringer +} + +// Use tracks the use of a value and its parent instruction or terminator. +type Use struct { + // Use of value. + Val *value.Value + // Parent instruction or terminator which uses value as operand. + User User // ir.Instruction or ir.Terminator +} + +// Replace replaces the use of a value with the new value. +func (use *Use) Replace(new value.Value) { + *use.Val = new +} + +// FuncUses returns value uses of instructions and terminators in the given +// function. +func FuncUses(f *ir.Func) []*Use { + var uses []*Use + for _, block := range f.Blocks { + for _, inst := range block.Insts { + us := InstUses(inst) + uses = append(uses, us...) + } + us := TermUses(block.Term) + uses = append(uses, us...) + } + return uses +} + +// TODO: consider renaming InstUses to InstOperands. + +// InstUses returns value uses of the given instruction. +func InstUses(inst ir.Instruction) []*Use { + switch inst := inst.(type) { + // Unary instructions + case *ir.InstFNeg: + return []*Use{ + {Val: &inst.X, User: inst}, + } + // Binary instructions + case *ir.InstAdd: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstFAdd: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstSub: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstFSub: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstMul: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstFMul: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstUDiv: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstSDiv: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstFDiv: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstURem: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstSRem: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstFRem: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + // Bitwise instructions + case *ir.InstShl: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstLShr: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstAShr: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstAnd: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstOr: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstXor: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + // Vector instructions + case *ir.InstExtractElement: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Index, User: inst}, + } + case *ir.InstInsertElement: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Elem, User: inst}, + {Val: &inst.Index, User: inst}, + } + case *ir.InstShuffleVector: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + {Val: &inst.Mask, User: inst}, + } + // Aggregate instructions + case *ir.InstExtractValue: + return []*Use{ + {Val: &inst.X, User: inst}, + } + case *ir.InstInsertValue: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Elem, User: inst}, + } + // Memory instructions + case *ir.InstAlloca: + var uses []*Use + if inst.NElems != nil { + use := &Use{Val: &inst.NElems, User: inst} + uses = append(uses, use) + } + return uses + case *ir.InstLoad: + return []*Use{ + {Val: &inst.Src, User: inst}, + } + case *ir.InstStore: + return []*Use{ + {Val: &inst.Src, User: inst}, + {Val: &inst.Dst, User: inst}, + } + case *ir.InstFence: + // no value operands. + return nil + case *ir.InstCmpXchg: + return []*Use{ + {Val: &inst.Ptr, User: inst}, + {Val: &inst.Cmp, User: inst}, + {Val: &inst.New, User: inst}, + } + case *ir.InstAtomicRMW: + return []*Use{ + {Val: &inst.Dst, User: inst}, + {Val: &inst.X, User: inst}, + } + case *ir.InstGetElementPtr: + uses := []*Use{ + {Val: &inst.Src, User: inst}, + } + for i := range inst.Indices { + use := &Use{Val: &inst.Indices[i], User: inst} + uses = append(uses, use) + } + return uses + // Conversion instructions + case *ir.InstTrunc: + return []*Use{ + {Val: &inst.From, User: inst}, + } + case *ir.InstZExt: + return []*Use{ + {Val: &inst.From, User: inst}, + } + case *ir.InstSExt: + return []*Use{ + {Val: &inst.From, User: inst}, + } + case *ir.InstFPTrunc: + return []*Use{ + {Val: &inst.From, User: inst}, + } + case *ir.InstFPExt: + return []*Use{ + {Val: &inst.From, User: inst}, + } + case *ir.InstFPToUI: + return []*Use{ + {Val: &inst.From, User: inst}, + } + case *ir.InstFPToSI: + return []*Use{ + {Val: &inst.From, User: inst}, + } + case *ir.InstUIToFP: + return []*Use{ + {Val: &inst.From, User: inst}, + } + case *ir.InstSIToFP: + return []*Use{ + {Val: &inst.From, User: inst}, + } + case *ir.InstPtrToInt: + return []*Use{ + {Val: &inst.From, User: inst}, + } + case *ir.InstIntToPtr: + return []*Use{ + {Val: &inst.From, User: inst}, + } + case *ir.InstBitCast: + return []*Use{ + {Val: &inst.From, User: inst}, + } + case *ir.InstAddrSpaceCast: + return []*Use{ + {Val: &inst.From, User: inst}, + } + // Other instructions + case *ir.InstICmp: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstFCmp: + return []*Use{ + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstPhi: + var uses []*Use + for i := range inst.Incs { + use := &Use{Val: &inst.Incs[i].X, User: inst} + uses = append(uses, use) + use = &Use{Val: &inst.Incs[i].Pred, User: inst} + uses = append(uses, use) + } + return uses + case *ir.InstSelect: + return []*Use{ + {Val: &inst.Cond, User: inst}, + {Val: &inst.X, User: inst}, + {Val: &inst.Y, User: inst}, + } + case *ir.InstCall: + uses := []*Use{ + {Val: &inst.Callee, User: inst}, + } + for i := range inst.Args { + use := &Use{Val: &inst.Args[i], User: inst} + uses = append(uses, use) + } + for i := range inst.OperandBundles { + for j := range inst.OperandBundles[i].Inputs { + use := &Use{Val: &inst.OperandBundles[i].Inputs[j], User: inst} + uses = append(uses, use) + } + } + return uses + case *ir.InstVAArg: + return []*Use{ + {Val: &inst.ArgList, User: inst}, + } + case *ir.InstLandingPad: + var uses []*Use + for i := range inst.Clauses { + use := &Use{Val: &inst.Clauses[i].X, User: inst} + uses = append(uses, use) + } + return uses + case *ir.InstCatchPad: + uses := []*Use{ + {Val: &inst.Scope, User: inst}, + } + for i := range inst.Args { + use := &Use{Val: &inst.Args[i], User: inst} + uses = append(uses, use) + } + return uses + case *ir.InstCleanupPad: + uses := []*Use{ + {Val: &inst.Scope, User: inst}, + } + for i := range inst.Args { + use := &Use{Val: &inst.Args[i], User: inst} + uses = append(uses, use) + } + return uses + default: + panic(fmt.Errorf("support for instruction %T not yet implemented", inst)) + } +} + +// TermUses returns value uses of the given terminator. +func TermUses(term ir.Terminator) []*Use { + switch term := term.(type) { + case *ir.TermRet: + var uses []*Use + if term.X != nil { + use := &Use{Val: &term.X, User: term} + uses = append(uses, use) + } + return uses + case *ir.TermBr: + return []*Use{ + {Val: &term.Target, User: term}, + } + case *ir.TermCondBr: + return []*Use{ + {Val: &term.Cond, User: term}, + {Val: &term.TargetTrue, User: term}, + {Val: &term.TargetFalse, User: term}, + } + case *ir.TermSwitch: + uses := []*Use{ + {Val: &term.X, User: term}, + {Val: &term.TargetDefault, User: term}, + } + for i := range term.Cases { + use := &Use{Val: &term.Cases[i].X, User: term} + uses = append(uses, use) + use = &Use{Val: &term.Cases[i].Target, User: term} + uses = append(uses, use) + } + return uses + case *ir.TermIndirectBr: + uses := []*Use{ + {Val: &term.Addr, User: term}, + } + for i := range term.ValidTargets { + use := &Use{Val: &term.ValidTargets[i], User: term} + uses = append(uses, use) + } + return uses + case *ir.TermInvoke: + uses := []*Use{ + {Val: &term.Invokee, User: term}, + } + for i := range term.Args { + use := &Use{Val: &term.Args[i], User: term} + uses = append(uses, use) + } + use := &Use{Val: &term.Normal, User: term} + uses = append(uses, use) + use = &Use{Val: &term.Exception, User: term} + uses = append(uses, use) + for i := range term.OperandBundles { + for j := range term.OperandBundles[i].Inputs { + use := &Use{Val: &term.OperandBundles[i].Inputs[j], User: term} + uses = append(uses, use) + } + } + return uses + case *ir.TermCallBr: + uses := []*Use{ + {Val: &term.Callee, User: term}, + } + for i := range term.Args { + use := &Use{Val: &term.Args[i], User: term} + uses = append(uses, use) + } + use := &Use{Val: &term.Normal, User: term} + uses = append(uses, use) + for i := range term.Others { + use := &Use{Val: &term.Others[i], User: term} + uses = append(uses, use) + } + for i := range term.OperandBundles { + for j := range term.OperandBundles[i].Inputs { + use := &Use{Val: &term.OperandBundles[i].Inputs[j], User: term} + uses = append(uses, use) + } + } + return uses + case *ir.TermResume: + return []*Use{ + {Val: &term.X, User: term}, + } + case *ir.TermCatchSwitch: + uses := []*Use{ + {Val: &term.Scope, User: term}, + } + for i := range term.Handlers { + use := &Use{Val: &term.Handlers[i], User: term} + uses = append(uses, use) + } + use := &Use{Val: &term.UnwindTarget, User: term} + uses = append(uses, use) + return uses + case *ir.TermCatchRet: + return []*Use{ + {Val: &term.From, User: term}, + {Val: &term.To, User: term}, + } + case *ir.TermCleanupRet: + return []*Use{ + {Val: &term.From, User: term}, + {Val: &term.UnwindTarget, User: term}, + } + case *ir.TermUnreachable: + // no value operands. + return nil + default: + panic(fmt.Errorf("support for terminator %T not yet implemented", term)) + } +} From 118a899f75dabef8004b8c2d26027e9464d66c7b Mon Sep 17 00:00:00 2001 From: Robin Eklind Date: Sun, 22 Dec 2019 07:11:22 +0100 Subject: [PATCH 02/11] irutil: add preliminary support for Comment pseudo-instruciton in InstUses Note: we should try to arrive at a more general way of handling custom user-defined instructions. --- use.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/use.go b/use.go index b8b8c23..22ee9a9 100644 --- a/use.go +++ b/use.go @@ -338,6 +338,12 @@ func InstUses(inst ir.Instruction) []*Use { uses = append(uses, use) } return uses + // TODO: figure out how to handle user defined instructions (e.g. Comment). + // TODO: remove *Comment case and add support for more general way of + // identifying user-defined instructions. + case *Comment: + // no value operands. + return nil default: panic(fmt.Errorf("support for instruction %T not yet implemented", inst)) } From 4d89531812d1ece48bede239fba7e9cc0381908c Mon Sep 17 00:00:00 2001 From: Robin Eklind Date: Mon, 23 Dec 2019 01:34:19 +0100 Subject: [PATCH 03/11] irutil: add backing slice to avoid memory allocation in use-tracking API As suggested by @pwaller in https://github.com/llir/irutil/pull/1#discussion_r360733838 --- use.go | 486 +++++++++++++++++++++++---------------------------------- 1 file changed, 197 insertions(+), 289 deletions(-) diff --git a/use.go b/use.go index 22ee9a9..c759bc1 100644 --- a/use.go +++ b/use.go @@ -31,313 +31,249 @@ func (use *Use) Replace(new value.Value) { } // FuncUses returns value uses of instructions and terminators in the given -// function. -func FuncUses(f *ir.Func) []*Use { - var uses []*Use +// function. To avoid memory allocation, an optional backing slice may be +// provided; if nil or insufficient space, a new backing slice will be +// allocated. +func FuncUses(backing []*Use, f *ir.Func) []*Use { for _, block := range f.Blocks { for _, inst := range block.Insts { - us := InstUses(inst) - uses = append(uses, us...) + backing = InstUses(backing, inst) } - us := TermUses(block.Term) - uses = append(uses, us...) + backing = TermUses(backing, block.Term) } - return uses + return backing } // TODO: consider renaming InstUses to InstOperands. -// InstUses returns value uses of the given instruction. -func InstUses(inst ir.Instruction) []*Use { +// InstUses returns value uses of the given instruction. To avoid memory +// allocation, an optional backing slice may be provided; if nil or insufficient +// space, a new backing slice will be allocated. +func InstUses(backing []*Use, inst ir.Instruction) []*Use { switch inst := inst.(type) { // Unary instructions case *ir.InstFNeg: - return []*Use{ - {Val: &inst.X, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + return backing // Binary instructions case *ir.InstAdd: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstFAdd: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstSub: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstFSub: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstMul: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstFMul: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstUDiv: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstSDiv: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstFDiv: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstURem: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstSRem: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstFRem: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing // Bitwise instructions case *ir.InstShl: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstLShr: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstAShr: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstAnd: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstOr: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstXor: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing // Vector instructions case *ir.InstExtractElement: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Index, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Index, User: inst}) + return backing case *ir.InstInsertElement: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Elem, User: inst}, - {Val: &inst.Index, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Elem, User: inst}) + backing = append(backing, &Use{Val: &inst.Index, User: inst}) + return backing case *ir.InstShuffleVector: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - {Val: &inst.Mask, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, &Use{Val: &inst.Mask, User: inst}) + return backing // Aggregate instructions case *ir.InstExtractValue: - return []*Use{ - {Val: &inst.X, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + return backing case *ir.InstInsertValue: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Elem, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Elem, User: inst}) + return backing // Memory instructions case *ir.InstAlloca: - var uses []*Use if inst.NElems != nil { - use := &Use{Val: &inst.NElems, User: inst} - uses = append(uses, use) + backing = append(backing, &Use{Val: &inst.NElems, User: inst}) } - return uses + return backing case *ir.InstLoad: - return []*Use{ - {Val: &inst.Src, User: inst}, - } + backing = append(backing, &Use{Val: &inst.Src, User: inst}) + return backing case *ir.InstStore: - return []*Use{ - {Val: &inst.Src, User: inst}, - {Val: &inst.Dst, User: inst}, - } + backing = append(backing, &Use{Val: &inst.Src, User: inst}) + backing = append(backing, &Use{Val: &inst.Dst, User: inst}) + return backing case *ir.InstFence: // no value operands. return nil case *ir.InstCmpXchg: - return []*Use{ - {Val: &inst.Ptr, User: inst}, - {Val: &inst.Cmp, User: inst}, - {Val: &inst.New, User: inst}, - } + backing = append(backing, &Use{Val: &inst.Ptr, User: inst}) + backing = append(backing, &Use{Val: &inst.Cmp, User: inst}) + backing = append(backing, &Use{Val: &inst.New, User: inst}) + return backing case *ir.InstAtomicRMW: - return []*Use{ - {Val: &inst.Dst, User: inst}, - {Val: &inst.X, User: inst}, - } + backing = append(backing, &Use{Val: &inst.Dst, User: inst}) + backing = append(backing, &Use{Val: &inst.X, User: inst}) + return backing case *ir.InstGetElementPtr: - uses := []*Use{ - {Val: &inst.Src, User: inst}, - } + backing = append(backing, &Use{Val: &inst.Src, User: inst}) for i := range inst.Indices { - use := &Use{Val: &inst.Indices[i], User: inst} - uses = append(uses, use) + backing = append(backing, &Use{Val: &inst.Indices[i], User: inst}) } - return uses + return backing // Conversion instructions case *ir.InstTrunc: - return []*Use{ - {Val: &inst.From, User: inst}, - } + backing = append(backing, &Use{Val: &inst.From, User: inst}) + return backing case *ir.InstZExt: - return []*Use{ - {Val: &inst.From, User: inst}, - } + backing = append(backing, &Use{Val: &inst.From, User: inst}) + return backing case *ir.InstSExt: - return []*Use{ - {Val: &inst.From, User: inst}, - } + backing = append(backing, &Use{Val: &inst.From, User: inst}) + return backing case *ir.InstFPTrunc: - return []*Use{ - {Val: &inst.From, User: inst}, - } + backing = append(backing, &Use{Val: &inst.From, User: inst}) + return backing case *ir.InstFPExt: - return []*Use{ - {Val: &inst.From, User: inst}, - } + backing = append(backing, &Use{Val: &inst.From, User: inst}) + return backing case *ir.InstFPToUI: - return []*Use{ - {Val: &inst.From, User: inst}, - } + backing = append(backing, &Use{Val: &inst.From, User: inst}) + return backing case *ir.InstFPToSI: - return []*Use{ - {Val: &inst.From, User: inst}, - } + backing = append(backing, &Use{Val: &inst.From, User: inst}) + return backing case *ir.InstUIToFP: - return []*Use{ - {Val: &inst.From, User: inst}, - } + backing = append(backing, &Use{Val: &inst.From, User: inst}) + return backing case *ir.InstSIToFP: - return []*Use{ - {Val: &inst.From, User: inst}, - } + backing = append(backing, &Use{Val: &inst.From, User: inst}) + return backing case *ir.InstPtrToInt: - return []*Use{ - {Val: &inst.From, User: inst}, - } + backing = append(backing, &Use{Val: &inst.From, User: inst}) + return backing case *ir.InstIntToPtr: - return []*Use{ - {Val: &inst.From, User: inst}, - } + backing = append(backing, &Use{Val: &inst.From, User: inst}) + return backing case *ir.InstBitCast: - return []*Use{ - {Val: &inst.From, User: inst}, - } + backing = append(backing, &Use{Val: &inst.From, User: inst}) + return backing case *ir.InstAddrSpaceCast: - return []*Use{ - {Val: &inst.From, User: inst}, - } + backing = append(backing, &Use{Val: &inst.From, User: inst}) + return backing // Other instructions case *ir.InstICmp: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstFCmp: - return []*Use{ - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstPhi: - var uses []*Use for i := range inst.Incs { - use := &Use{Val: &inst.Incs[i].X, User: inst} - uses = append(uses, use) - use = &Use{Val: &inst.Incs[i].Pred, User: inst} - uses = append(uses, use) + backing = append(backing, &Use{Val: &inst.Incs[i].X, User: inst}) + backing = append(backing, &Use{Val: &inst.Incs[i].Pred, User: inst}) } - return uses + return backing case *ir.InstSelect: - return []*Use{ - {Val: &inst.Cond, User: inst}, - {Val: &inst.X, User: inst}, - {Val: &inst.Y, User: inst}, - } + backing = append(backing, &Use{Val: &inst.Cond, User: inst}) + backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, &Use{Val: &inst.Y, User: inst}) + return backing case *ir.InstCall: - uses := []*Use{ - {Val: &inst.Callee, User: inst}, - } + backing = append(backing, &Use{Val: &inst.Callee, User: inst}) for i := range inst.Args { - use := &Use{Val: &inst.Args[i], User: inst} - uses = append(uses, use) + backing = append(backing, &Use{Val: &inst.Args[i], User: inst}) } for i := range inst.OperandBundles { for j := range inst.OperandBundles[i].Inputs { - use := &Use{Val: &inst.OperandBundles[i].Inputs[j], User: inst} - uses = append(uses, use) + backing = append(backing, &Use{Val: &inst.OperandBundles[i].Inputs[j], User: inst}) } } - return uses + return backing case *ir.InstVAArg: - return []*Use{ - {Val: &inst.ArgList, User: inst}, - } + backing = append(backing, &Use{Val: &inst.ArgList, User: inst}) + return backing case *ir.InstLandingPad: - var uses []*Use for i := range inst.Clauses { - use := &Use{Val: &inst.Clauses[i].X, User: inst} - uses = append(uses, use) + backing = append(backing, &Use{Val: &inst.Clauses[i].X, User: inst}) } - return uses + return backing case *ir.InstCatchPad: - uses := []*Use{ - {Val: &inst.Scope, User: inst}, - } + backing = append(backing, &Use{Val: &inst.Scope, User: inst}) for i := range inst.Args { - use := &Use{Val: &inst.Args[i], User: inst} - uses = append(uses, use) + backing = append(backing, &Use{Val: &inst.Args[i], User: inst}) } - return uses + return backing case *ir.InstCleanupPad: - uses := []*Use{ - {Val: &inst.Scope, User: inst}, - } + backing = append(backing, &Use{Val: &inst.Scope, User: inst}) for i := range inst.Args { - use := &Use{Val: &inst.Args[i], User: inst} - uses = append(uses, use) + backing = append(backing, &Use{Val: &inst.Args[i], User: inst}) } - return uses + return backing // TODO: figure out how to handle user defined instructions (e.g. Comment). // TODO: remove *Comment case and add support for more general way of // identifying user-defined instructions. @@ -349,112 +285,84 @@ func InstUses(inst ir.Instruction) []*Use { } } -// TermUses returns value uses of the given terminator. -func TermUses(term ir.Terminator) []*Use { +// TermUses returns value uses of the given terminator. To avoid memory +// allocation, an optional backing slice may be provided; if nil or insufficient +// space, a new backing slice will be allocated. +func TermUses(backing []*Use, term ir.Terminator) []*Use { switch term := term.(type) { case *ir.TermRet: - var uses []*Use if term.X != nil { - use := &Use{Val: &term.X, User: term} - uses = append(uses, use) + backing = append(backing, &Use{Val: &term.X, User: term}) } - return uses + return backing case *ir.TermBr: - return []*Use{ - {Val: &term.Target, User: term}, - } + backing = append(backing, &Use{Val: &term.Target, User: term}) + return backing case *ir.TermCondBr: - return []*Use{ - {Val: &term.Cond, User: term}, - {Val: &term.TargetTrue, User: term}, - {Val: &term.TargetFalse, User: term}, - } + backing = append(backing, &Use{Val: &term.Cond, User: term}) + backing = append(backing, &Use{Val: &term.TargetTrue, User: term}) + backing = append(backing, &Use{Val: &term.TargetFalse, User: term}) + return backing case *ir.TermSwitch: - uses := []*Use{ - {Val: &term.X, User: term}, - {Val: &term.TargetDefault, User: term}, - } + backing = append(backing, &Use{Val: &term.X, User: term}) + backing = append(backing, &Use{Val: &term.TargetDefault, User: term}) for i := range term.Cases { - use := &Use{Val: &term.Cases[i].X, User: term} - uses = append(uses, use) - use = &Use{Val: &term.Cases[i].Target, User: term} - uses = append(uses, use) + backing = append(backing, &Use{Val: &term.Cases[i].X, User: term}) + backing = append(backing, &Use{Val: &term.Cases[i].Target, User: term}) } - return uses + return backing case *ir.TermIndirectBr: - uses := []*Use{ - {Val: &term.Addr, User: term}, - } + backing = append(backing, &Use{Val: &term.Addr, User: term}) for i := range term.ValidTargets { - use := &Use{Val: &term.ValidTargets[i], User: term} - uses = append(uses, use) + backing = append(backing, &Use{Val: &term.ValidTargets[i], User: term}) } - return uses + return backing case *ir.TermInvoke: - uses := []*Use{ - {Val: &term.Invokee, User: term}, - } + backing = append(backing, &Use{Val: &term.Invokee, User: term}) for i := range term.Args { - use := &Use{Val: &term.Args[i], User: term} - uses = append(uses, use) + backing = append(backing, &Use{Val: &term.Args[i], User: term}) } - use := &Use{Val: &term.Normal, User: term} - uses = append(uses, use) - use = &Use{Val: &term.Exception, User: term} - uses = append(uses, use) + backing = append(backing, &Use{Val: &term.Normal, User: term}) + backing = append(backing, &Use{Val: &term.Exception, User: term}) for i := range term.OperandBundles { for j := range term.OperandBundles[i].Inputs { - use := &Use{Val: &term.OperandBundles[i].Inputs[j], User: term} - uses = append(uses, use) + backing = append(backing, &Use{Val: &term.OperandBundles[i].Inputs[j], User: term}) } } - return uses + return backing case *ir.TermCallBr: - uses := []*Use{ - {Val: &term.Callee, User: term}, - } + backing = append(backing, &Use{Val: &term.Callee, User: term}) for i := range term.Args { - use := &Use{Val: &term.Args[i], User: term} - uses = append(uses, use) + backing = append(backing, &Use{Val: &term.Args[i], User: term}) } - use := &Use{Val: &term.Normal, User: term} - uses = append(uses, use) + backing = append(backing, &Use{Val: &term.Normal, User: term}) for i := range term.Others { - use := &Use{Val: &term.Others[i], User: term} - uses = append(uses, use) + backing = append(backing, &Use{Val: &term.Others[i], User: term}) } for i := range term.OperandBundles { for j := range term.OperandBundles[i].Inputs { - use := &Use{Val: &term.OperandBundles[i].Inputs[j], User: term} - uses = append(uses, use) + backing = append(backing, &Use{Val: &term.OperandBundles[i].Inputs[j], User: term}) } } - return uses + return backing case *ir.TermResume: - return []*Use{ - {Val: &term.X, User: term}, - } + backing = append(backing, &Use{Val: &term.X, User: term}) + return backing case *ir.TermCatchSwitch: - uses := []*Use{ - {Val: &term.Scope, User: term}, - } + backing = append(backing, &Use{Val: &term.Scope, User: term}) for i := range term.Handlers { - use := &Use{Val: &term.Handlers[i], User: term} - uses = append(uses, use) + backing = append(backing, &Use{Val: &term.Handlers[i], User: term}) } - use := &Use{Val: &term.UnwindTarget, User: term} - uses = append(uses, use) - return uses + backing = append(backing, &Use{Val: &term.UnwindTarget, User: term}) + return backing case *ir.TermCatchRet: - return []*Use{ - {Val: &term.From, User: term}, - {Val: &term.To, User: term}, - } + backing = append(backing, &Use{Val: &term.From, User: term}) + backing = append(backing, &Use{Val: &term.To, User: term}) + return backing case *ir.TermCleanupRet: - return []*Use{ - {Val: &term.From, User: term}, - {Val: &term.UnwindTarget, User: term}, - } + backing = append(backing, &Use{Val: &term.From, User: term}) + backing = append(backing, &Use{Val: &term.UnwindTarget, User: term}) + return backing case *ir.TermUnreachable: // no value operands. return nil From a8eb359500676c68b55882ab913c334b8b7992ba Mon Sep 17 00:00:00 2001 From: Robin Eklind Date: Mon, 23 Dec 2019 01:36:11 +0100 Subject: [PATCH 04/11] irutil: return backing slice also from instructions which have no value operands Without this change, repeated invokations of InstUses (as used in FuncUses) would result in incorrect results. for _, inst := range block.Insts { backing = InstUses(backing, inst) } --- use.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/use.go b/use.go index c759bc1..aed4ad2 100644 --- a/use.go +++ b/use.go @@ -167,7 +167,7 @@ func InstUses(backing []*Use, inst ir.Instruction) []*Use { return backing case *ir.InstFence: // no value operands. - return nil + return backing case *ir.InstCmpXchg: backing = append(backing, &Use{Val: &inst.Ptr, User: inst}) backing = append(backing, &Use{Val: &inst.Cmp, User: inst}) @@ -279,7 +279,7 @@ func InstUses(backing []*Use, inst ir.Instruction) []*Use { // identifying user-defined instructions. case *Comment: // no value operands. - return nil + return backing default: panic(fmt.Errorf("support for instruction %T not yet implemented", inst)) } @@ -365,7 +365,7 @@ func TermUses(backing []*Use, term ir.Terminator) []*Use { return backing case *ir.TermUnreachable: // no value operands. - return nil + return backing default: panic(fmt.Errorf("support for terminator %T not yet implemented", term)) } From 9e472c03e97de09a0541f78d09c8ee002f31b55b Mon Sep 17 00:00:00 2001 From: Robin Eklind Date: Mon, 23 Dec 2019 01:39:06 +0100 Subject: [PATCH 05/11] irutil: refine use-tracking API to operate on []Use instead of []*Use Since we already track a pointer to the used value in the Use structure, we can pass Use by value. This should help reduce memory allocations. --- use.go | 246 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 123 insertions(+), 123 deletions(-) diff --git a/use.go b/use.go index aed4ad2..ee998eb 100644 --- a/use.go +++ b/use.go @@ -26,7 +26,7 @@ type Use struct { } // Replace replaces the use of a value with the new value. -func (use *Use) Replace(new value.Value) { +func (use Use) Replace(new value.Value) { *use.Val = new } @@ -34,7 +34,7 @@ func (use *Use) Replace(new value.Value) { // function. To avoid memory allocation, an optional backing slice may be // provided; if nil or insufficient space, a new backing slice will be // allocated. -func FuncUses(backing []*Use, f *ir.Func) []*Use { +func FuncUses(backing []Use, f *ir.Func) []Use { for _, block := range f.Blocks { for _, inst := range block.Insts { backing = InstUses(backing, inst) @@ -49,229 +49,229 @@ func FuncUses(backing []*Use, f *ir.Func) []*Use { // InstUses returns value uses of the given instruction. To avoid memory // allocation, an optional backing slice may be provided; if nil or insufficient // space, a new backing slice will be allocated. -func InstUses(backing []*Use, inst ir.Instruction) []*Use { +func InstUses(backing []Use, inst ir.Instruction) []Use { switch inst := inst.(type) { // Unary instructions case *ir.InstFNeg: - backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) return backing // Binary instructions case *ir.InstAdd: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstFAdd: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstSub: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstFSub: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstMul: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstFMul: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstUDiv: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstSDiv: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstFDiv: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstURem: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstSRem: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstFRem: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing // Bitwise instructions case *ir.InstShl: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstLShr: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstAShr: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstAnd: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstOr: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstXor: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing // Vector instructions case *ir.InstExtractElement: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Index, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Index, User: inst}) return backing case *ir.InstInsertElement: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Elem, User: inst}) - backing = append(backing, &Use{Val: &inst.Index, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Elem, User: inst}) + backing = append(backing, Use{Val: &inst.Index, User: inst}) return backing case *ir.InstShuffleVector: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) - backing = append(backing, &Use{Val: &inst.Mask, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.Mask, User: inst}) return backing // Aggregate instructions case *ir.InstExtractValue: - backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) return backing case *ir.InstInsertValue: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Elem, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Elem, User: inst}) return backing // Memory instructions case *ir.InstAlloca: if inst.NElems != nil { - backing = append(backing, &Use{Val: &inst.NElems, User: inst}) + backing = append(backing, Use{Val: &inst.NElems, User: inst}) } return backing case *ir.InstLoad: - backing = append(backing, &Use{Val: &inst.Src, User: inst}) + backing = append(backing, Use{Val: &inst.Src, User: inst}) return backing case *ir.InstStore: - backing = append(backing, &Use{Val: &inst.Src, User: inst}) - backing = append(backing, &Use{Val: &inst.Dst, User: inst}) + backing = append(backing, Use{Val: &inst.Src, User: inst}) + backing = append(backing, Use{Val: &inst.Dst, User: inst}) return backing case *ir.InstFence: // no value operands. return backing case *ir.InstCmpXchg: - backing = append(backing, &Use{Val: &inst.Ptr, User: inst}) - backing = append(backing, &Use{Val: &inst.Cmp, User: inst}) - backing = append(backing, &Use{Val: &inst.New, User: inst}) + backing = append(backing, Use{Val: &inst.Ptr, User: inst}) + backing = append(backing, Use{Val: &inst.Cmp, User: inst}) + backing = append(backing, Use{Val: &inst.New, User: inst}) return backing case *ir.InstAtomicRMW: - backing = append(backing, &Use{Val: &inst.Dst, User: inst}) - backing = append(backing, &Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Dst, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) return backing case *ir.InstGetElementPtr: - backing = append(backing, &Use{Val: &inst.Src, User: inst}) + backing = append(backing, Use{Val: &inst.Src, User: inst}) for i := range inst.Indices { - backing = append(backing, &Use{Val: &inst.Indices[i], User: inst}) + backing = append(backing, Use{Val: &inst.Indices[i], User: inst}) } return backing // Conversion instructions case *ir.InstTrunc: - backing = append(backing, &Use{Val: &inst.From, User: inst}) + backing = append(backing, Use{Val: &inst.From, User: inst}) return backing case *ir.InstZExt: - backing = append(backing, &Use{Val: &inst.From, User: inst}) + backing = append(backing, Use{Val: &inst.From, User: inst}) return backing case *ir.InstSExt: - backing = append(backing, &Use{Val: &inst.From, User: inst}) + backing = append(backing, Use{Val: &inst.From, User: inst}) return backing case *ir.InstFPTrunc: - backing = append(backing, &Use{Val: &inst.From, User: inst}) + backing = append(backing, Use{Val: &inst.From, User: inst}) return backing case *ir.InstFPExt: - backing = append(backing, &Use{Val: &inst.From, User: inst}) + backing = append(backing, Use{Val: &inst.From, User: inst}) return backing case *ir.InstFPToUI: - backing = append(backing, &Use{Val: &inst.From, User: inst}) + backing = append(backing, Use{Val: &inst.From, User: inst}) return backing case *ir.InstFPToSI: - backing = append(backing, &Use{Val: &inst.From, User: inst}) + backing = append(backing, Use{Val: &inst.From, User: inst}) return backing case *ir.InstUIToFP: - backing = append(backing, &Use{Val: &inst.From, User: inst}) + backing = append(backing, Use{Val: &inst.From, User: inst}) return backing case *ir.InstSIToFP: - backing = append(backing, &Use{Val: &inst.From, User: inst}) + backing = append(backing, Use{Val: &inst.From, User: inst}) return backing case *ir.InstPtrToInt: - backing = append(backing, &Use{Val: &inst.From, User: inst}) + backing = append(backing, Use{Val: &inst.From, User: inst}) return backing case *ir.InstIntToPtr: - backing = append(backing, &Use{Val: &inst.From, User: inst}) + backing = append(backing, Use{Val: &inst.From, User: inst}) return backing case *ir.InstBitCast: - backing = append(backing, &Use{Val: &inst.From, User: inst}) + backing = append(backing, Use{Val: &inst.From, User: inst}) return backing case *ir.InstAddrSpaceCast: - backing = append(backing, &Use{Val: &inst.From, User: inst}) + backing = append(backing, Use{Val: &inst.From, User: inst}) return backing // Other instructions case *ir.InstICmp: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstFCmp: - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstPhi: for i := range inst.Incs { - backing = append(backing, &Use{Val: &inst.Incs[i].X, User: inst}) - backing = append(backing, &Use{Val: &inst.Incs[i].Pred, User: inst}) + backing = append(backing, Use{Val: &inst.Incs[i].X, User: inst}) + backing = append(backing, Use{Val: &inst.Incs[i].Pred, User: inst}) } return backing case *ir.InstSelect: - backing = append(backing, &Use{Val: &inst.Cond, User: inst}) - backing = append(backing, &Use{Val: &inst.X, User: inst}) - backing = append(backing, &Use{Val: &inst.Y, User: inst}) + backing = append(backing, Use{Val: &inst.Cond, User: inst}) + backing = append(backing, Use{Val: &inst.X, User: inst}) + backing = append(backing, Use{Val: &inst.Y, User: inst}) return backing case *ir.InstCall: - backing = append(backing, &Use{Val: &inst.Callee, User: inst}) + backing = append(backing, Use{Val: &inst.Callee, User: inst}) for i := range inst.Args { - backing = append(backing, &Use{Val: &inst.Args[i], User: inst}) + backing = append(backing, Use{Val: &inst.Args[i], User: inst}) } for i := range inst.OperandBundles { for j := range inst.OperandBundles[i].Inputs { - backing = append(backing, &Use{Val: &inst.OperandBundles[i].Inputs[j], User: inst}) + backing = append(backing, Use{Val: &inst.OperandBundles[i].Inputs[j], User: inst}) } } return backing case *ir.InstVAArg: - backing = append(backing, &Use{Val: &inst.ArgList, User: inst}) + backing = append(backing, Use{Val: &inst.ArgList, User: inst}) return backing case *ir.InstLandingPad: for i := range inst.Clauses { - backing = append(backing, &Use{Val: &inst.Clauses[i].X, User: inst}) + backing = append(backing, Use{Val: &inst.Clauses[i].X, User: inst}) } return backing case *ir.InstCatchPad: - backing = append(backing, &Use{Val: &inst.Scope, User: inst}) + backing = append(backing, Use{Val: &inst.Scope, User: inst}) for i := range inst.Args { - backing = append(backing, &Use{Val: &inst.Args[i], User: inst}) + backing = append(backing, Use{Val: &inst.Args[i], User: inst}) } return backing case *ir.InstCleanupPad: - backing = append(backing, &Use{Val: &inst.Scope, User: inst}) + backing = append(backing, Use{Val: &inst.Scope, User: inst}) for i := range inst.Args { - backing = append(backing, &Use{Val: &inst.Args[i], User: inst}) + backing = append(backing, Use{Val: &inst.Args[i], User: inst}) } return backing // TODO: figure out how to handle user defined instructions (e.g. Comment). @@ -288,80 +288,80 @@ func InstUses(backing []*Use, inst ir.Instruction) []*Use { // TermUses returns value uses of the given terminator. To avoid memory // allocation, an optional backing slice may be provided; if nil or insufficient // space, a new backing slice will be allocated. -func TermUses(backing []*Use, term ir.Terminator) []*Use { +func TermUses(backing []Use, term ir.Terminator) []Use { switch term := term.(type) { case *ir.TermRet: if term.X != nil { - backing = append(backing, &Use{Val: &term.X, User: term}) + backing = append(backing, Use{Val: &term.X, User: term}) } return backing case *ir.TermBr: - backing = append(backing, &Use{Val: &term.Target, User: term}) + backing = append(backing, Use{Val: &term.Target, User: term}) return backing case *ir.TermCondBr: - backing = append(backing, &Use{Val: &term.Cond, User: term}) - backing = append(backing, &Use{Val: &term.TargetTrue, User: term}) - backing = append(backing, &Use{Val: &term.TargetFalse, User: term}) + backing = append(backing, Use{Val: &term.Cond, User: term}) + backing = append(backing, Use{Val: &term.TargetTrue, User: term}) + backing = append(backing, Use{Val: &term.TargetFalse, User: term}) return backing case *ir.TermSwitch: - backing = append(backing, &Use{Val: &term.X, User: term}) - backing = append(backing, &Use{Val: &term.TargetDefault, User: term}) + backing = append(backing, Use{Val: &term.X, User: term}) + backing = append(backing, Use{Val: &term.TargetDefault, User: term}) for i := range term.Cases { - backing = append(backing, &Use{Val: &term.Cases[i].X, User: term}) - backing = append(backing, &Use{Val: &term.Cases[i].Target, User: term}) + backing = append(backing, Use{Val: &term.Cases[i].X, User: term}) + backing = append(backing, Use{Val: &term.Cases[i].Target, User: term}) } return backing case *ir.TermIndirectBr: - backing = append(backing, &Use{Val: &term.Addr, User: term}) + backing = append(backing, Use{Val: &term.Addr, User: term}) for i := range term.ValidTargets { - backing = append(backing, &Use{Val: &term.ValidTargets[i], User: term}) + backing = append(backing, Use{Val: &term.ValidTargets[i], User: term}) } return backing case *ir.TermInvoke: - backing = append(backing, &Use{Val: &term.Invokee, User: term}) + backing = append(backing, Use{Val: &term.Invokee, User: term}) for i := range term.Args { - backing = append(backing, &Use{Val: &term.Args[i], User: term}) + backing = append(backing, Use{Val: &term.Args[i], User: term}) } - backing = append(backing, &Use{Val: &term.Normal, User: term}) - backing = append(backing, &Use{Val: &term.Exception, User: term}) + backing = append(backing, Use{Val: &term.Normal, User: term}) + backing = append(backing, Use{Val: &term.Exception, User: term}) for i := range term.OperandBundles { for j := range term.OperandBundles[i].Inputs { - backing = append(backing, &Use{Val: &term.OperandBundles[i].Inputs[j], User: term}) + backing = append(backing, Use{Val: &term.OperandBundles[i].Inputs[j], User: term}) } } return backing case *ir.TermCallBr: - backing = append(backing, &Use{Val: &term.Callee, User: term}) + backing = append(backing, Use{Val: &term.Callee, User: term}) for i := range term.Args { - backing = append(backing, &Use{Val: &term.Args[i], User: term}) + backing = append(backing, Use{Val: &term.Args[i], User: term}) } - backing = append(backing, &Use{Val: &term.Normal, User: term}) + backing = append(backing, Use{Val: &term.Normal, User: term}) for i := range term.Others { - backing = append(backing, &Use{Val: &term.Others[i], User: term}) + backing = append(backing, Use{Val: &term.Others[i], User: term}) } for i := range term.OperandBundles { for j := range term.OperandBundles[i].Inputs { - backing = append(backing, &Use{Val: &term.OperandBundles[i].Inputs[j], User: term}) + backing = append(backing, Use{Val: &term.OperandBundles[i].Inputs[j], User: term}) } } return backing case *ir.TermResume: - backing = append(backing, &Use{Val: &term.X, User: term}) + backing = append(backing, Use{Val: &term.X, User: term}) return backing case *ir.TermCatchSwitch: - backing = append(backing, &Use{Val: &term.Scope, User: term}) + backing = append(backing, Use{Val: &term.Scope, User: term}) for i := range term.Handlers { - backing = append(backing, &Use{Val: &term.Handlers[i], User: term}) + backing = append(backing, Use{Val: &term.Handlers[i], User: term}) } - backing = append(backing, &Use{Val: &term.UnwindTarget, User: term}) + backing = append(backing, Use{Val: &term.UnwindTarget, User: term}) return backing case *ir.TermCatchRet: - backing = append(backing, &Use{Val: &term.From, User: term}) - backing = append(backing, &Use{Val: &term.To, User: term}) + backing = append(backing, Use{Val: &term.From, User: term}) + backing = append(backing, Use{Val: &term.To, User: term}) return backing case *ir.TermCleanupRet: - backing = append(backing, &Use{Val: &term.From, User: term}) - backing = append(backing, &Use{Val: &term.UnwindTarget, User: term}) + backing = append(backing, Use{Val: &term.From, User: term}) + backing = append(backing, Use{Val: &term.UnwindTarget, User: term}) return backing case *ir.TermUnreachable: // no value operands. From 224a42353d866d722f04ad656a09c1fdbce72f33 Mon Sep 17 00:00:00 2001 From: Robin Eklind Date: Tue, 24 Dec 2019 00:59:01 +0100 Subject: [PATCH 06/11] irutil: use llir/llvm (instead of mewpull/llvm) now that llir/llvm#122 is merged --- go.mod | 7 ++++--- go.sum | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 3a28439..d1546ac 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,7 @@ module github.com/llir/irutil go 1.12 -require github.com/llir/llvm v0.3.0-pre7.0.20191220191840-fc6d0e8d9bc6 - -replace github.com/llir/llvm => github.com/mewpull/llvm v0.3.0-pre7.0.20191221041541-3f58af5a0014 +require ( + github.com/llir/llvm v0.3.0-pre7.0.20191223202448-41e3d8af7b94 + github.com/pkg/errors v0.8.1 +) diff --git a/go.sum b/go.sum index 8912cd7..fce2e0b 100644 --- a/go.sum +++ b/go.sum @@ -7,12 +7,12 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/llir/ll v0.0.0-20191205054844-50f4b743547b h1:Hc7BLGoz4hbN+8mb+JhZeYS+5mE8l+L+KWp7cJVeNrY= github.com/llir/ll v0.0.0-20191205054844-50f4b743547b/go.mod h1:8W5HJz80PitAyPZUpOcljQxTu6LD5YKW1URTo+OjVoc= +github.com/llir/llvm v0.3.0-pre7.0.20191223202448-41e3d8af7b94 h1:VVz0Q2MiQv++0GoF+yHOxW41Wuyo83wxBb8inmiyu6c= +github.com/llir/llvm v0.3.0-pre7.0.20191223202448-41e3d8af7b94/go.mod h1:w6wEy+jHlAsmT+yQGYOvL1TO+JEDlzt+NnsYegCunuo= github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2 h1:EyTNMdePWaoWsRSGQnXiSoQu0r6RS1eA557AwJhlzHU= github.com/mewkiz/pkg v0.0.0-20190919212034-518ade7978e2/go.mod h1:3E2FUC/qYUfM8+r9zAwpeHJzqRVVMIYnpzD/clwWxyA= github.com/mewmew/float v0.0.0-20191218075745-e26b3d092977 h1:6+YqyNydbRNcd0azoFfSM36F1O5nbc0M3OzKqsT8YCA= github.com/mewmew/float v0.0.0-20191218075745-e26b3d092977/go.mod h1:O+xb+8ycBNHzJicFVs7GRWtruD4tVZI0huVnw5TM01E= -github.com/mewpull/llvm v0.3.0-pre7.0.20191221041541-3f58af5a0014 h1:STFmB7NoMfxXapksFUH+EwOTcruUQ+jaeFxZU+KiXL4= -github.com/mewpull/llvm v0.3.0-pre7.0.20191221041541-3f58af5a0014/go.mod h1:w6wEy+jHlAsmT+yQGYOvL1TO+JEDlzt+NnsYegCunuo= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= From a88117e6fa4af0e46334bea701c1044322a04a98 Mon Sep 17 00:00:00 2001 From: Robin Eklind Date: Tue, 24 Dec 2019 01:00:16 +0100 Subject: [PATCH 07/11] irutil: add dead code elimination as use-tracking API example Note: this implementation of dce is a toy example. It does not take deep value uses into account, and as such, cannot handle use tracking when local variables are used within constant expressions. This issue would be resolved when irutil.FuncUses is extended to handle deep value use tracking. --- example_test.go | 152 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 example_test.go diff --git a/example_test.go b/example_test.go new file mode 100644 index 0000000..bd04ba7 --- /dev/null +++ b/example_test.go @@ -0,0 +1,152 @@ +package irutil_test + +import ( + "fmt" + "log" + + "github.com/llir/irutil" + "github.com/llir/llvm/asm" + "github.com/llir/llvm/ir" + "github.com/llir/llvm/ir/value" + "github.com/pkg/errors" +) + +func Example() { + // Parse LLVM IR module. + const module = ` +define void @g() { + ; potential side effects + ret void +} + +define i32 @f(i32 %x, i32 %y) { +;