llvm2alive: Add support for constant exprs
MODIFY
llvm_util/known_fns.cpp
llvm_util/known_fns.cpp
#include "llvm_util/utils.h"
#include "ir/function.h"
#include "ir/instr.h"
+#include "llvm/IR/Constants.h"
#include "llvm/Analysis/MemoryBuiltins.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include <vector>
@@ -19,14 +20,15 @@ namespace llvm_util {
pair<unique_ptr<Instr>, bool>
known_call(llvm::CallInst &i, const llvm::TargetLibraryInfo &TLI,
- BasicBlock &BB) {
+ BasicBlock &BB,
+ function<Instr*(llvm::ConstantExpr *)> constexpr_conv) {
auto ty = llvm_type2alive(i.getType());
if (!ty)
RETURN_FAIL_KNOWN();
vector<Value*> args;
for (auto &arg : i.args()) {
- auto a = get_operand(arg);
+ auto a = get_operand(arg, constexpr_conv);
if (!a)
RETURN_FAIL_KNOWN();
args.emplace_back(a);

MODIFY
llvm_util/known_fns.h
llvm_util/known_fns.h
// Distributed under the MIT license that can be found in the LICENSE file.
#include "ir/function.h"
+#include <functional>
#include <memory>
#include <utility>
namespace llvm {
class CallInst;
+class ConstantExpr;
class TargetLibraryInfo;
}
@@ -22,6 +24,7 @@ namespace llvm_util {
// returned bool indicates whether it's a known function call
std::pair<std::unique_ptr<IR::Instr>, bool>
known_call(llvm::CallInst &i, const llvm::TargetLibraryInfo &TLI,
- IR::BasicBlock &BB);
+ IR::BasicBlock &BB,
+ std::function<IR::Instr*(llvm::ConstantExpr *)> constexpr_conv);
}

MODIFY
llvm_util/llvm2alive.cpp
llvm_util/llvm2alive.cpp
#include "llvm/IR/GetElementPtrTypeIterator.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/IR/Operator.h"
+#include <sstream>
#include <utility>
#include <vector>
@@ -58,6 +59,8 @@ class llvm2alive_ : public llvm::InstVisitor<llvm2alive_, unique_ptr<Instr>> {
BasicBlock *BB;
llvm::Function &f;
const llvm::TargetLibraryInfo &TLI;
+ vector<llvm::Instruction *> i_constexprs;
+
using RetTy = unique_ptr<Instr>;
auto DL() const { return f.getParent()->getDataLayout(); }
@@ -74,10 +77,36 @@ class llvm2alive_ : public llvm::InstVisitor<llvm2alive_, unique_ptr<Instr>> {
return a != 0 ? a : DL().getPrefTypeAlignment(ty);
}
+ auto convert_constexpr(llvm::ConstantExpr *cexpr) {
+ llvm::Instruction *newI = cexpr->getAsInstruction();
+ static unsigned constexpr_idx = 0;
+ stringstream ss;
+ ss << "__constexpr_" << constexpr_idx++;
+ newI->setName(ss.str());
+
+ i_constexprs.push_back(newI);
+ auto ptr = this->visit(*newI);
+ auto i = ptr.get();
+ BB->addInstr(move(ptr));
+ return i;
+ }
+
+ auto get_operand(llvm::Value *v) {
+ return llvm_util::get_operand(v,
+ [this](auto I) { return convert_constexpr(I); });
+ }
+
public:
llvm2alive_(llvm::Function &f, const llvm::TargetLibraryInfo &TLI)
: f(f), TLI(TLI) {}
+ ~llvm2alive_() {
+ for (auto &inst : i_constexprs) {
+ remove_value_name(*inst); // otherwise value_names maintain freed pointers
+ inst->deleteValue();
+ }
+ }
+
RetTy visitUnaryOperator(llvm::UnaryOperator &i) {
PARSE_UNOP();
UnaryOp::Op op;
@@ -173,7 +202,8 @@ public:
}
RetTy visitCallInst(llvm::CallInst &i) {
- auto [call_val, known] = known_call(i, TLI, *BB);
+ auto [call_val, known] = known_call(i, TLI, *BB,
+ [this](auto I) { return convert_constexpr(I); });
if (call_val)
RETURN_IDENTIFIER(move(call_val));

MODIFY
llvm_util/utils.cpp
llvm_util/utils.cpp
@@ -60,6 +60,12 @@ string value_name(const llvm::Value &v) {
: '%' + to_string(value_id_counter++);
}
+void remove_value_name(const llvm::Value &v) {
+ auto itr = value_names.find(&v);
+ if (itr != value_names.end())
+ value_names.erase(itr);
+}
+
Type& get_int_type(unsigned bits) {
if (bits >= int_types.size())
int_types.resize(bits + 1);
@@ -138,7 +144,8 @@ Value* make_intconst(uint64_t val, int bits) {
}
-Value* get_operand(llvm::Value *v) {
+Value* get_operand(llvm::Value *v,
+ function<Instr*(llvm::ConstantExpr *)> constexpr_conv) {
if (isa<llvm::Instruction>(v) || isa<llvm::Argument>(v))
return identifiers[v];
@@ -209,7 +216,10 @@ Value* get_operand(llvm::Value *v) {
}
Value *initval = nullptr;
if (gv->hasInitializer() && gv->isConstant()) {
- if (!(initval = get_operand(gv->getInitializer())))
+ if (isa<llvm::ConstantExpr>(gv->getInitializer()))
+ // TODO: not supported
+ return nullptr;
+ else if (!(initval = get_operand(gv->getInitializer(), constexpr_conv)))
return nullptr;
}
int size = DL->getTypeAllocSize(gv->getValueType());
@@ -226,7 +236,7 @@ Value* get_operand(llvm::Value *v) {
if (auto cnst = dyn_cast<llvm::ConstantAggregate>(v)) {
vector<Value*> vals;
for (auto I = cnst->op_begin(), E = cnst->op_end(); I != E; ++I) {
- if (auto op = get_operand(*I))
+ if (auto op = get_operand(*I, constexpr_conv))
vals.emplace_back(op);
else
return nullptr;
@@ -240,7 +250,7 @@ Value* get_operand(llvm::Value *v) {
if (auto cnst = dyn_cast<llvm::ConstantDataSequential>(v)) {
vector<Value*> vals;
for (unsigned i = 0, e = cnst->getNumElements(); i != e; ++i) {
- if (auto op = get_operand(cnst->getElementAsConstant(i)))
+ if (auto op = get_operand(cnst->getElementAsConstant(i), constexpr_conv))
vals.emplace_back(op);
else
return nullptr;
@@ -254,7 +264,7 @@ Value* get_operand(llvm::Value *v) {
if (auto cnst = dyn_cast<llvm::ConstantAggregateZero>(v)) {
vector<Value*> vals;
for (unsigned i = 0, e = cnst->getNumElements(); i != e; ++i) {
- if (auto op = get_operand(cnst->getElementValue(i)))
+ if (auto op = get_operand(cnst->getElementValue(i), constexpr_conv))
vals.emplace_back(op);
else
return nullptr;
@@ -265,6 +275,10 @@ Value* get_operand(llvm::Value *v) {
return ret;
}
+ if (auto cexpr = dyn_cast<llvm::ConstantExpr>(v)) {
+ return constexpr_conv(cexpr);
+ }
+
return nullptr;
}

MODIFY
llvm_util/utils.h
llvm_util/utils.h
#include <ostream>
#include <string>
+#include <functional>
namespace llvm {
class BasicBlock;
+class ConstantExpr;
class DataLayout;
class Type;
class Value;
@@ -16,6 +18,7 @@ class Value;
namespace IR {
class BasicBlock;
class Function;
+class Instr;
class Type;
class Value;
}
@@ -25,12 +28,14 @@ namespace llvm_util {
IR::BasicBlock& getBB(const llvm::BasicBlock *bb);
std::string value_name(const llvm::Value &v);
+void remove_value_name(const llvm::Value &v);
IR::Type& get_int_type(unsigned bits);
IR::Type* llvm_type2alive(const llvm::Type *ty);
IR::Value* make_intconst(uint64_t val, int bits);
-IR::Value* get_operand(llvm::Value *v);
+IR::Value* get_operand(llvm::Value *v,
+ std::function<IR::Instr*(llvm::ConstantExpr *)> constexpr_conv);
void add_identifier(const llvm::Value &llvm, IR::Value &v);

ADD
tests/alive-tv/constexpr/loadgep-fail.src.ll
+@g = constant i16 1
+
+define i8 @f() {
+ %x = load i8, i8* getelementptr inbounds (i8, i8* bitcast (i16* @g to i8*), i64 1)
+ ; After conversion, it becomes
+ ; %1 = bitcast * @g to *
+ ; %2 = gep inbounds * %1, 1 x i64 1
+ ; %x = load i8, * %2, align 1
+ ret i8 %x
+}
+
+; ERROR: Value mismatch

ADD
tests/alive-tv/constexpr/loadgep-fail.tgt.ll
+@g = constant i16 1
+
+define i8 @f() {
+ %1 = bitcast i16* @g to i8*
+ %2 = getelementptr inbounds i8, i8* %1, i64 0
+ %x = load i8, i8* %2, align 1
+ ret i8 %x
+}

ADD
tests/alive-tv/constexpr/loadgep.src.ll
+@g = constant i16 0
+
+define i8 @f() {
+ %x = load i8, i8* getelementptr inbounds (i8, i8* bitcast (i16* @g to i8*), i64 1)
+ ret i8 %x
+}

ADD
tests/alive-tv/constexpr/loadgep.tgt.ll
+@g = constant i16 0
+
+define i8 @f() {
+ %1 = bitcast i16* @g to i8*
+ %2 = getelementptr inbounds i8, i8* %1, i64 1
+ %x = load i8, i8* %2, align 1
+ ret i8 0
+}

ADD
tests/alive-tv/constexpr/phigep-fail.src.ll
+@g = constant i16 1
+
+define i8 @f(i1 %cond) {
+ br i1 %cond, label %A, label %B
+A:
+ br label %EXIT
+B:
+ br label %EXIT
+EXIT:
+ %addr = phi i8* [getelementptr inbounds (i8, i8* bitcast (i16* @g to i8*), i64 0), %A],
+ [getelementptr inbounds (i8, i8* bitcast (i16* @g to i8*), i64 1), %B]
+ %x = load i8, i8* %addr
+ ret i8 %x
+}
+
+; ERROR: Value mismatch

ADD
tests/alive-tv/constexpr/phigep-fail.tgt.ll
+@g = constant i16 1
+
+define i8 @f(i1 %cond) {
+ br i1 %cond, label %A, label %B
+A:
+ %g1 = bitcast i16* @g to i8*
+ %a1 = getelementptr inbounds i8, i8* %g1, i64 1
+ br label %EXIT
+B:
+ %g2 = bitcast i16* @g to i8*
+ %a2 = getelementptr inbounds i8, i8* %g2, i64 0
+ br label %EXIT
+EXIT:
+ %addr = phi i8* [%a1, %A], [%a2, %B]
+ %x = load i8, i8* %addr
+ ret i8 %x
+}

ADD
tests/alive-tv/constexpr/phigep.src.ll
+@g = constant i16 0
+
+define i8 @f(i1 %cond) {
+ br i1 %cond, label %A, label %B
+A:
+ br label %EXIT
+B:
+ br label %EXIT
+EXIT:
+; %__constexpr_0 = bitcast * @g to *
+; %__constexpr_1 = bitcast * @g to *
+; %__constexpr_2 = gep inbounds * %__constexpr_1, 1 x i64 1
+; %addr = phi * [ %__constexpr_0, %A ], [ %__constexpr_2, %B ]
+ %addr = phi i8* [getelementptr inbounds (i8, i8* bitcast (i16* @g to i8*), i64 0), %A],
+ [getelementptr inbounds (i8, i8* bitcast (i16* @g to i8*), i64 1), %B]
+ %x = load i8, i8* %addr
+ ret i8 %x
+}

ADD
tests/alive-tv/constexpr/phigep.tgt.ll
+@g = constant i16 0
+
+define i8 @f(i1 %cond) {
+ br i1 %cond, label %A, label %B
+A:
+ %g1 = bitcast i16* @g to i8*
+ %a1 = getelementptr inbounds i8, i8* %g1, i64 0
+ br label %EXIT
+B:
+ %g2 = bitcast i16* @g to i8*
+ %a2 = getelementptr inbounds i8, i8* %g2, i64 1
+ br label %EXIT
+EXIT:
+ %addr = phi i8* [%a1, %A], [%a2, %B]
+ %x = load i8, i8* %addr
+ ret i8 %x
+}

ADD
tests/alive-tv/constexpr/ptrdiff.src.ll
+target datalayout = "p:64:64:64"
+@g = constant i8 0
+@h = constant i8 0
+
+define i64 @f() {
+ ret i64 sub (i64 ptrtoint (i8* getelementptr inbounds (i8, i8* @g, i64 1) to i64), i64 ptrtoint (i8* @h to i64))
+}

ADD
tests/alive-tv/constexpr/ptrdiff.tgt.ll
+target datalayout = "p:64:64:64"
+@g = constant i8 0
+@h = constant i8 0
+
+define i64 @f() {
+ ret i64 add (i64 sub (i64 ptrtoint (i8* @g to i64), i64 ptrtoint (i8* @h to i64)), i64 1)
+}

llvm2alive: Add support for constant exprs (#181)