Skip to content

Commit 1d4e20a

Browse files
authored
Merge pull request #585 from secure-software-engineering/f-lcaSwift
intrinsicBinaryOp, GetElementPtr, and ExtractValue support for IDE LCA
2 parents b1a9431 + fc14af9 commit 1d4e20a

14 files changed

Lines changed: 312 additions & 22 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ jobs:
6060
sudo apt download libclang-rt-14-dev
6161
sudo dpkg --force-all -i libclang-rt-14-dev*
6262
63+
- uses: swift-actions/setup-swift@v1
6364
- name: Building Phasar in ${{ matrix.build }} with ${{ matrix.compiler[0] }}
6465
env:
6566
BUILD_TYPE: ${{ matrix.build }}
@@ -72,6 +73,7 @@ jobs:
7273
cmake .. \
7374
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
7475
-DCMAKE_CXX_COMPILER=$CXX \
76+
-DBUILD_SWIFT_TESTS=1 \
7577
-G Ninja
7678
cmake --build .
7779

CMakeLists.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ include("phasar_macros")
5353

5454
option(PHASAR_BUILD_UNITTESTS "Build all tests (default is ON)" ON)
5555

56+
option(BUILD_SWIFT_TESTS "Builds the Swift tests (Swift compiler has to be installed manually beforehand!)" OFF)
57+
if (BUILD_SWIFT_TESTS)
58+
set(CMAKE_Swift_FLAGS_RELEASE "-g")
59+
set(CMAKE_Swift_FLAGS_RELWITHDEBINFO "-g")
60+
enable_language(Swift)
61+
endif(BUILD_SWIFT_TESTS)
62+
5663
option(PHASAR_BUILD_OPENSSL_TS_UNITTESTS "Build OPENSSL typestate tests (require OpenSSL, default is OFF)" OFF)
5764

5865
option(PHASAR_BUILD_IR "Build IR test code (default is ON)" ON)

include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,38 @@ template <typename Fn, typename Container = std::set<const llvm::Value *>,
386386
std::is_invocable_r_v<bool, Fn, const llvm::Value *>>>
387387
FlowFunctionPtrType<const llvm::Value *, Container>
388388
strongUpdateStore(const llvm::StoreInst *Store, Fn &&GeneratePointerOpIf) {
389+
// Here we cheat a bit and "look through" the GetElementPtrInst to the
390+
// targeted memory location.
391+
const auto *BasePtrOp = Store->getPointerOperand()->stripPointerCasts();
392+
if (BasePtrOp != Store->getPointerOperand()) {
393+
struct StrongUpdateFlow
394+
: public FlowFunction<const llvm::Value *, Container> {
395+
396+
StrongUpdateFlow(const llvm::Value *PointerOp,
397+
const llvm::Value *BasePtrOp, Fn &&GeneratePointerOpIf)
398+
: PointerOp(PointerOp), BasePtrOp(BasePtrOp),
399+
Pred(std::forward<Fn>(GeneratePointerOpIf)) {}
400+
401+
Container computeTargets(const llvm::Value *Source) override {
402+
if (Source == PointerOp || Source == BasePtrOp) {
403+
return {};
404+
}
405+
if (std::invoke(Pred, Source)) {
406+
return {Source, PointerOp, BasePtrOp};
407+
}
408+
return {Source};
409+
}
410+
411+
const llvm::Value *PointerOp;
412+
const llvm::Value *BasePtrOp;
413+
[[no_unique_address]] std::decay_t<Fn> Pred;
414+
};
415+
416+
return std::make_shared<StrongUpdateFlow>(
417+
Store->getPointerOperand(), BasePtrOp,
418+
std::forward<Fn>(GeneratePointerOpIf));
419+
}
420+
389421
struct StrongUpdateFlow
390422
: public FlowFunction<const llvm::Value *, Container> {
391423

@@ -431,10 +463,38 @@ strongUpdateStore(const llvm::StoreInst *Store, Fn &&GeneratePointerOpIf) {
431463
template <typename Container = std::set<const llvm::Value *>>
432464
FlowFunctionPtrType<const llvm::Value *, Container>
433465
strongUpdateStore(const llvm::StoreInst *Store) {
466+
// Here we cheat a bit and "look through" the GetElementPtrInst to the
467+
// targeted memory location.
468+
const auto *BasePtrOp = Store->getPointerOperand()->stripPointerCasts();
469+
if (BasePtrOp != Store->getPointerOperand()) {
470+
struct StrongUpdateFlow
471+
: public FlowFunction<const llvm::Value *, Container> {
472+
473+
StrongUpdateFlow(const llvm::StoreInst *Store,
474+
const llvm::Value *BasePtrOp) noexcept
475+
: Store(Store), BasePtrOp(BasePtrOp) {}
476+
477+
Container computeTargets(const llvm::Value *Source) override {
478+
if (Source == Store->getPointerOperand() || Source == BasePtrOp) {
479+
return {};
480+
}
481+
if (Source == Store->getValueOperand()) {
482+
return {Source, Store->getPointerOperand(), BasePtrOp};
483+
}
484+
return {Source};
485+
}
486+
487+
const llvm::StoreInst *Store;
488+
const llvm::Value *BasePtrOp;
489+
};
490+
491+
return std::make_shared<StrongUpdateFlow>(Store, BasePtrOp);
492+
}
493+
434494
struct StrongUpdateFlow
435495
: public FlowFunction<const llvm::Value *, Container> {
436496

437-
StrongUpdateFlow(const llvm::StoreInst *Store) : Store(Store) {}
497+
StrongUpdateFlow(const llvm::StoreInst *Store) noexcept : Store(Store) {}
438498

439499
Container computeTargets(const llvm::Value *Source) override {
440500
if (Source == Store->getPointerOperand()) {

include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,13 @@ class LLVMZeroValue : public llvm::GlobalVariable {
4949
return LLVMZeroValueInternalName;
5050
}
5151

52-
static bool isLLVMZeroValue(const llvm::Value *V) {
53-
return V == getInstance();
54-
}
55-
5652
// Do not specify a destructor (at all)!
5753
static const LLVMZeroValue *getInstance();
54+
55+
// NOLINTNEXTLINE(readability-identifier-naming)
56+
static constexpr auto isLLVMZeroValue = [](const llvm::Value *V) {
57+
return V == getInstance();
58+
};
5859
};
5960
} // namespace psr
6061

include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,11 @@ class IDELinearConstantAnalysis
6565
std::string SrcNode;
6666
std::map<std::string, l_t> VariableToValue;
6767
std::vector<n_t> IRTrace;
68-
void print(llvm::raw_ostream &OS);
68+
void print(llvm::raw_ostream &OS) const;
6969
inline bool operator==(const LCAResult &Rhs) const {
7070
return SrcNode == Rhs.SrcNode && VariableToValue == Rhs.VariableToValue &&
7171
IRTrace == Rhs.IRTrace;
7272
}
73-
74-
operator std::string() const;
7573
};
7674

7775
using lca_results_t = std::map<std::string, std::map<unsigned, LCAResult>>;
@@ -91,6 +89,8 @@ class IDELinearConstantAnalysis
9189
getCallToRetFlowFunction(n_t CallSite, n_t RetSite,
9290
llvm::ArrayRef<f_t> Callees) override;
9391

92+
FlowFunctionPtrType getSummaryFlowFunction(n_t Curr, f_t CalleeFun) override;
93+
9494
[[nodiscard]] InitialSeeds<n_t, d_t, l_t> initialSeeds() override;
9595

9696
[[nodiscard]] d_t createZeroValue() const;
@@ -116,6 +116,10 @@ class IDELinearConstantAnalysis
116116
d_t RetSiteNode,
117117
llvm::ArrayRef<f_t> Callees) override;
118118

119+
std::shared_ptr<EdgeFunction<l_t>>
120+
getSummaryEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ,
121+
d_t SuccNode) override;
122+
119123
std::shared_ptr<EdgeFunction<l_t>> allTopFunction() override;
120124

121125
// Helper functions

lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.cpp

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "llvm/IR/InstrTypes.h"
3131
#include "llvm/IR/Instruction.h"
3232
#include "llvm/IR/Instructions.h"
33+
#include "llvm/IR/IntrinsicInst.h"
3334
#include "llvm/IR/LLVMContext.h"
3435
#include "llvm/IR/Type.h"
3536
#include "llvm/IR/Value.h"
@@ -296,20 +297,25 @@ IDELinearConstantAnalysis::getNormalFlowFunction(n_t Curr, n_t /*Succ*/) {
296297
d_t ValueOp = Store->getValueOperand();
297298
// Case I: Storing a constant integer.
298299
if (llvm::isa<llvm::ConstantInt>(ValueOp)) {
299-
// return Identity<d_t>::getInstance();
300-
return strongUpdateStore(Store, [](d_t Source) {
301-
return LLVMZeroValue::isLLVMZeroValue(Source);
302-
});
300+
return strongUpdateStore(Store, LLVMZeroValue::isLLVMZeroValue);
303301
}
302+
304303
// Case II: Storing an integer typed value.
305304
if (ValueOp->getType()->isIntegerTy()) {
306305
return strongUpdateStore(Store);
307306
}
308307
}
308+
309+
if (const auto *GEP = llvm::dyn_cast<llvm::GetElementPtrInst>(Curr)) {
310+
if (GEP->getResultElementType()->isIntegerTy()) {
311+
const auto *Op = GEP->getPointerOperand();
312+
return generateFlow(GEP, Op);
313+
}
314+
}
309315
// check load instructions
310316
if (const auto *Load = llvm::dyn_cast<llvm::LoadInst>(Curr)) {
311317
// only consider i32 load
312-
if (Load->getPointerOperandType()->getPointerElementType()->isIntegerTy()) {
318+
if (Load->getType()->isIntegerTy()) {
313319
return generateFlowIf<d_t>(Load, [Load](d_t Source) {
314320
return Source == Load->getPointerOperand();
315321
});
@@ -327,6 +333,21 @@ IDELinearConstantAnalysis::getNormalFlowFunction(n_t Curr, n_t /*Succ*/) {
327333
llvm::isa<llvm::ConstantInt>(Rop));
328334
});
329335
}
336+
337+
if (const auto *Extract = llvm::dyn_cast<llvm::ExtractValueInst>(Curr)) {
338+
const auto *Agg = Extract->getAggregateOperand();
339+
340+
/// We are extracting the result of a BinaryOpIntrinsic
341+
/// The first parameter holds the resulting integer if
342+
/// no error occured during the operation
343+
if (const auto *BinIntrinsic =
344+
llvm::dyn_cast<llvm::BinaryOpIntrinsic>(Agg)) {
345+
if (Extract->getType()->isIntegerTy()) {
346+
return generateFlow<d_t>(Curr, Agg);
347+
}
348+
}
349+
}
350+
330351
return identityFlow<d_t>();
331352
}
332353

@@ -417,6 +438,25 @@ IDELinearConstantAnalysis::initialSeeds() {
417438
return Seeds;
418439
}
419440

441+
IDELinearConstantAnalysis::FlowFunctionPtrType
442+
IDELinearConstantAnalysis::getSummaryFlowFunction(n_t Curr, f_t /*CalleeFun*/) {
443+
444+
if (const auto *BinIntrinsic =
445+
llvm::dyn_cast<llvm::BinaryOpIntrinsic>(Curr)) {
446+
auto *Lop = BinIntrinsic->getLHS();
447+
auto *Rop = BinIntrinsic->getRHS();
448+
449+
return generateFlowIf<d_t>(BinIntrinsic, [this, Lop, Rop](d_t Source) {
450+
/// Intentionally include nonlinear operations here for being able to
451+
/// explicitly set them to BOTTOM in the edge function
452+
return (Lop == Source) || (Rop == Source) ||
453+
(isZeroValue(Source) && llvm::isa<llvm::ConstantInt>(Lop) &&
454+
llvm::isa<llvm::ConstantInt>(Rop));
455+
});
456+
}
457+
return nullptr;
458+
}
459+
420460
IDELinearConstantAnalysis::d_t
421461
IDELinearConstantAnalysis::createZeroValue() const {
422462
// create a special value to represent the zero value!
@@ -538,6 +578,29 @@ IDELinearConstantAnalysis::getCallToRetEdgeFunction(
538578
return EdgeIdentity<l_t>::getInstance();
539579
}
540580

581+
std::shared_ptr<EdgeFunction<IDELinearConstantAnalysis::l_t>>
582+
IDELinearConstantAnalysis::getSummaryEdgeFunction(n_t Curr, d_t CurrNode,
583+
n_t /*Succ*/, d_t SuccNode) {
584+
585+
if (const auto *BinIntrinsic =
586+
llvm::dyn_cast<llvm::BinaryOpIntrinsic>(Curr)) {
587+
auto *Lop = BinIntrinsic->getLHS();
588+
auto *Rop = BinIntrinsic->getRHS();
589+
unsigned OP = BinIntrinsic->getBinaryOp();
590+
591+
// For non linear constant computation we propagate bottom
592+
if ((CurrNode == Lop && !llvm::isa<llvm::ConstantInt>(Rop)) ||
593+
(CurrNode == Rop && !llvm::isa<llvm::ConstantInt>(Lop))) {
594+
return AllBottom<l_t>::getInstance();
595+
}
596+
597+
if (Curr == SuccNode && CurrNode != SuccNode) {
598+
return std::make_shared<lca::BinOp>(OP, Lop, Rop, CurrNode);
599+
}
600+
}
601+
return EdgeIdentity<l_t>::getInstance();
602+
}
603+
541604
std::shared_ptr<EdgeFunction<IDELinearConstantAnalysis::l_t>>
542605
IDELinearConstantAnalysis::allTopFunction() {
543606
return std::make_shared<AllTop<l_t>>();
@@ -709,13 +772,7 @@ IDELinearConstantAnalysis::getLCAResults(SolverResults<n_t, d_t, l_t> SR) {
709772
return AggResults;
710773
}
711774

712-
void IDELinearConstantAnalysis::LCAResult::print(llvm::raw_ostream &OS) {
713-
OS << this;
714-
}
715-
716-
IDELinearConstantAnalysis::LCAResult::operator std::string() const {
717-
std::string Buffer;
718-
llvm::raw_string_ostream OS(Buffer);
775+
void IDELinearConstantAnalysis::LCAResult::print(llvm::raw_ostream &OS) const {
719776
OS << "Line " << LineNr << ": " << SrcNode << '\n';
720777
OS << "Var(s): ";
721778
for (auto It = VariableToValue.begin(); It != VariableToValue.end(); ++It) {
@@ -728,7 +785,6 @@ IDELinearConstantAnalysis::LCAResult::operator std::string() const {
728785
for (const auto *Ir : IRTrace) {
729786
OS << " " << llvmIRToString(Ir) << '\n';
730787
}
731-
return Buffer;
732788
}
733789

734790
} // namespace psr

test/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,5 @@
11
add_subdirectory(llvm_test_code)
22
add_subdirectory(text_test_code)
3+
if(BUILD_SWIFT_TESTS)
4+
add_subdirectory(llvm_swift_test_code)
5+
endif()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
add_subdirectory(linear_constant)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
file(GLOB lca_files *.swift)
2+
3+
set(SWIFT_COMPILE_IR_FLAGS -emit-ir -suppress-warnings -g -parse-as-library -Onone -Xfrontend -disable-llvm-optzns -Xfrontend -disable-swift-specific-llvm-optzns)
4+
5+
foreach(TEST_SRC ${lca_files})
6+
get_filename_component(TEST_SRC_FILE ${TEST_SRC} NAME_WE)
7+
add_executable(${TEST_SRC_FILE}.ll ${TEST_SRC})
8+
target_compile_options(${TEST_SRC_FILE}.ll PRIVATE ${SWIFT_COMPILE_IR_FLAGS})
9+
endforeach(TEST_SRC)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
@main
2+
struct MyMain {
3+
4+
static func main() {
5+
// The code of this method can't be directly placed inside
6+
// the main function or it would be removed by the compiler
7+
// due to mandatory optimizations.
8+
var _ = simpleAdd(x: 1)
9+
}
10+
11+
static func simpleAdd(x: Int) -> Int {
12+
var a = x
13+
var b = a + 41
14+
return b
15+
}
16+
}

0 commit comments

Comments
 (0)