Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions llvm/examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ add_subdirectory(ModuleMaker)
add_subdirectory(OrcV2Examples)
add_subdirectory(SpeculativeJIT)
add_subdirectory(Bye)
add_subdirectory(ReverseStr)

if(LLVM_ENABLE_EH AND (NOT WIN32) AND (NOT "${LLVM_NATIVE_ARCH}" STREQUAL "ARM"))
add_subdirectory(ExceptionDemo)
Expand Down
17 changes: 17 additions & 0 deletions llvm/examples/ReverseStr/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# The plugin expects to not link against the Support and Core libraries,
# but expects them to exist in the process loading the plugin. This doesn't
# work with DLLs on Windows (where a shared library can't have undefined
# references), so just skip this example on Windows.
if (NOT WIN32 AND NOT CYGWIN)
add_llvm_pass_plugin(ReverseStr
ReverseGlobalStrPass.cpp
DEPENDS
intrinsics_gen
LLVMAnalysis

BUILDTREE_ONLY
)

install(TARGETS ${name} RUNTIME DESTINATION "${LLVM_EXAMPLES_INSTALL_DIR}")
set_target_properties(${name} PROPERTIES FOLDER "Examples")
endif()
42 changes: 42 additions & 0 deletions llvm/examples/ReverseStr/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Task
Write a plugin of opt and clang that can capture global variables of c-str in a translation unit and reverse the string literals.


```c
$cat t.c
#include <stdio.h>
const char * GLOBAL_CONSTANT_MSG = "hello world @ compile";
int main() {
puts(GLOBAL_CONSTANT_MSG);
}
```

# Approach
1. write a ModulePass using llvm.
2. idea
a. iterates all global variables of a module;
b. filter out whose type of initializer is an array of i8.
c. get the contents of initializer.
d. create a new Constant. the contents are the reversed string literal. replace the old initializer with the new Constant.

# Demo
```bash
$clang -emit-llvm -S t.c
$./bin/lli ./t.ll
hello world @ compile

$./bin/opt -load-pass-plugin=./lib/ReverseStr.so -passes=ReverseStr -debug ./t.ll > t.after.bc
Args: ./bin/opt -load-pass-plugin=./lib/ReverseStr.so -passes=ReverseStr -debug ./t.ll
global variable: @.str = private unnamed_addr constant [22 x i8] c"hello world @ compile\00", align 1
initializer: 0x556208a997a0
original c-string literal : hello world @ compile
after updated GV: @.str = private unnamed_addr constant [22 x i8] c"elipmoc @ dlrow olleh\00", align 1
global variable: @GLOBAL_CONSTANT_MSG = dso_local global ptr @.str, align 8

./bin/llvm-dis < t.after.bc| grep ^@
@.str = private unnamed_addr constant [22 x i8] c"elipmoc @ dlrow olleh\00", align 1
@GLOBAL_CONSTANT_MSG = dso_local global ptr @.str, align 8

$./bin/lli < t.after.bc
elipmoc @ dlrow olleh
```
109 changes: 109 additions & 0 deletions llvm/examples/ReverseStr/ReverseGlobalStrPass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include "llvm/IR/Module.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Pass.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Analysis/ConstantFolding.h"

#define DEBUG_TYPE "ReverseStr"
using namespace llvm;

namespace {
// only support latin-1? Do we need to support wide char?
static Constant *reverse_string_literal(LLVMContext &ctx, const StringRef & literal) {
size_t len = literal.size();
SmallVector<unsigned char, 256> RawBytes(len + 1);

for (size_t i=0; i < len; ++i) {
RawBytes[i] = literal[len - 1 - i];
}
RawBytes[len] = '\0';

return ConstantDataArray::get(ctx, RawBytes);
}

bool reverse_global_str(Module &M) {
LLVMContext &ctx = M.getContext();

for (auto &g: M.globals()) {
if (g.hasName() && g.hasInitializer()) {
LLVM_DEBUG(dbgs() << "global variable: " << g << '\n');

const Constant *c = g.getInitializer();
Type *ty = c->getType();
if(ty->isArrayTy() && cast<ArrayType>(ty)->getElementType()->isIntegerTy(8)/*i8*/) {
LLVM_DEBUG(dbgs() << "initializer: " << c << '\n');

Constant *contents= llvm::ReadByteArrayFromGlobal(&g, 0);
if (contents != nullptr) {
ConstantDataSequential *cds = cast<ConstantDataSequential>(contents);
if (cds->isCString()) {
StringRef literal = cds->getAsCString();
LLVM_DEBUG(dbgs() << "original c-string literal : " << literal << '\n');

if (!literal.empty()) {
auto reversed = reverse_string_literal(ctx, literal);
g.setInitializer(reversed);
LLVM_DEBUG(dbgs() << "after updated GV: " << g << '\n');
} else {
LLVM_DEBUG(dbgs() << "[empty] skipped!");
}
}
}
}
}
}
return true;
}


struct LegacyReverseStr : public ModulePass {
static char ID;
LegacyReverseStr() : ModulePass(ID) {}
bool runOnModule(Module &M) override { return reverse_global_str(M); }
};

struct ReverseGlobalStrPass: PassInfoMixin<ReverseGlobalStrPass> {
PreservedAnalyses run(Module &M, ModuleAnalysisManager &) {
reverse_global_str(M);
return PreservedAnalyses::none();
}

static bool isRequired() { return true; }
};

} // namespace

char LegacyReverseStr::ID = 0;

static RegisterPass<LegacyReverseStr> X("ReverseStr", "Reverse global variables of c-str",
false /* Only looks at CFG */,
false /* Analysis Pass */);

/* New PM Registration */
llvm::PassPluginLibraryInfo getReverseStrPluginInfo() {
return {LLVM_PLUGIN_API_VERSION, "ReverseStr", LLVM_VERSION_STRING,
[](PassBuilder &PB) {
PB.registerPipelineStartEPCallback(
[](llvm::ModulePassManager &PM, OptimizationLevel Level) {
PM.addPass(ReverseGlobalStrPass());
});
PB.registerPipelineParsingCallback(
[](StringRef Name, llvm::ModulePassManager &PM,
ArrayRef<llvm::PassBuilder::PipelineElement>) {
if (Name == "ReverseStr") {
PM.addPass(ReverseGlobalStrPass());
return true;
}
return false;
});
}};
}

extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
return getReverseStrPluginInfo();
}
5 changes: 5 additions & 0 deletions llvm/test/Transforms/HelloNew/reverse_cstr.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
; RUN: opt %loadnewpmreversestr -passes=ReverseStr < %s -S | FileCheck %s

; CHECK: c"elipmoc @ dlrow olleh\00"
@.str = private unnamed_addr constant [22 x i8] c"hello world @ compile\00", align 1
@GLOBAL_CONSTANT_MSG = dso_local global i8* getelementptr inbounds ([22 x i8], [22 x i8]* @.str, i32 0, i32 0), align 8
8 changes: 8 additions & 0 deletions llvm/test/lit.cfg.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,14 @@ def version_int(ver):
),
)
)
config.substitutions.append(
(
"%loadnewpmreversestr",
"-load-pass-plugin={}/ReverseStr{}".format(
config.llvm_shlib_dir, config.llvm_shlib_ext
),
)
)

if config.linked_exampleirtransforms_extension:
config.substitutions.append(("%loadexampleirtransforms", ""))
Expand Down