Compile golang code to IR and run IR edit program, then compile it to binary.
llvm
, gollvm
and related projectgit clone https://github.com/llvm/llvm-project.git
cd llvm-project/llvm/tools
git clone https://go.googlesource.com/gollvm
cd gollvm
git clone https://go.googlesource.com/gofrontend
cd libgo
git clone https://github.com/libffi/libffi.git
git clone https://github.com/ianlancetaylor/libbacktrace.git
cd llvm-project
mkdir build-gollvm
cd build-gollvm
cmake -DCMAKE_INSTALL_PREFIX=/home/zyh/Desktop/llvm_install -DCMAKE_BUILD_TYPE=Release -DLLVM_USE_LINKER=gold -G Ninja ../llvm
ninja gollvm
ninja install-gollvm
export LD_LIBRARY_PATH=/home/zyh/Desktop/llvm_install/lib64
export PATH=/home/zyh/Desktop/llvm_install/bin:$PATH
We have a simple go program
test.go
package main
import "fmt"
func add(x, y int) int {
return x + y
}
func main() {
var a int = 50000
var b int = 4
fmt.Println(add(a, b))
}
run test.go
go run test.go
50004
compile
llvm-goc -O0 -S -emit-llvm test.go
then we get test.ll
...
define internal i64 @main.add(i8* nest %nest.0, i64 %x, i64 %y) #0 !dbg !81 {
entry:
%x.addr = alloca i64
%y.addr = alloca i64
...
%x.ld.0 = load i64, i64* %x.addr, !dbg !90
%y.ld.0 = load i64, i64* %y.addr, !dbg !91
%add.0 = add i64 %x.ld.0, %y.ld.0, !dbg !92
store i64 %add.0, i64* %"$ret0", !dbg !93
...
}
...
We use a simple IR edit program which simply change add
to sub
//...
for (auto &i_i : bb->getInstList()) {
if (!i_i.isBinaryOp()) {
continue;
}
auto *i = dyn_cast<Instruction>(&i_i);
if (i != nullptr && i->getOpcode() == Instruction::Add) {
Instruction *new_i = BinaryOperator::CreateSub(i->getOperand(0), i->getOperand(1));
// replace the instruction later
instruction_replace.push_back(i);
instruction_new.push_back(new_i);
}
}
//...
// replace here
for (int i = 0; i < instruction_replace.size(); i++) {
ReplaceInstWithInst(instruction_replace[i], instruction_new[i]);
}
//...
run the program
./llvm-learn/p1/build/llvm-p1/llvm-p1 test.ll
then we get test_result.ll
...
define internal i64 @main.add(i8* nest %nest.0, i64 %x, i64 %y) #0 !dbg !81 {
entry:
%x.addr = alloca i64
%y.addr = alloca i64
...
%x.ld.0 = load i64, i64* %x.addr, !dbg !90
%y.ld.0 = load i64, i64* %y.addr, !dbg !91
%add.0 = sub i64 %x.ld.0, %y.ld.0, !dbg !92
store i64 %add.0, i64* %"$ret0", !dbg !93
...
}
...
llc test_result.ll
gccgo test_result.s
./a.out
# output is 49996