From f4030c34ec582b01d4cd2dd8236edfd0b531bcf9 Mon Sep 17 00:00:00 2001 From: d00573793 Date: Sun, 26 Feb 2023 22:49:00 +0800 Subject: [PATCH] [Update] Update to v0.4.1 (cherry picked from commit c9d9ea91d0ab8b6c0314646b9681085614841d99) --- 0001-Pin-gcc-client-Bugfix-for-ConstOp.patch | 34 - ...-refactoring-of-Communication-Subsys.patch | 2327 ---------------- ...-refactoring-of-Communication-Subsys.patch | 839 ------ ...-refactoring-of-Communication-Subsys.patch | 2397 ----------------- 0005-Pin-gcc-client-Add-DebugOp.patch | 106 - 0006-Pin-gcc-client-Bugfix-for-ConstOp.patch | 38 - ...gcc-client-Add-API-for-LTO-judgement.patch | 131 - ...n-gcc-client-Fix-bug-for-BuildCallOp.patch | 115 - ...t-Support-functiontype-structtype.eg.patch | 721 ----- pin-gcc-client-0.4.0.tar.gz | Bin 52554 -> 0 bytes pin-gcc-client-0.4.1.tar.gz | Bin 0 -> 70076 bytes pin-gcc-client.spec | 28 +- 12 files changed, 4 insertions(+), 6732 deletions(-) delete mode 100644 0001-Pin-gcc-client-Bugfix-for-ConstOp.patch delete mode 100644 0002-Refactoring-Code-refactoring-of-Communication-Subsys.patch delete mode 100644 0003-Refactoring-Code-refactoring-of-Communication-Subsys.patch delete mode 100644 0004-Refactoring-Code-refactoring-of-Communication-Subsys.patch delete mode 100644 0005-Pin-gcc-client-Add-DebugOp.patch delete mode 100644 0006-Pin-gcc-client-Bugfix-for-ConstOp.patch delete mode 100644 0007-Pin-gcc-client-Add-API-for-LTO-judgement.patch delete mode 100644 0008-Pin-gcc-client-Fix-bug-for-BuildCallOp.patch delete mode 100644 0009-Pin-gcc-client-Support-functiontype-structtype.eg.patch delete mode 100644 pin-gcc-client-0.4.0.tar.gz create mode 100644 pin-gcc-client-0.4.1.tar.gz diff --git a/0001-Pin-gcc-client-Bugfix-for-ConstOp.patch b/0001-Pin-gcc-client-Bugfix-for-ConstOp.patch deleted file mode 100644 index 5a4985e..0000000 --- a/0001-Pin-gcc-client-Bugfix-for-ConstOp.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 3bbf6ad7dd4cd77e98aa31f0c6bd41ba321b8290 Mon Sep 17 00:00:00 2001 -From: benniaobufeijiushiji -Date: Wed, 28 Dec 2022 15:04:28 +0800 -Subject: [PATCH 1/9] [Pin-gcc-client] Bugfix for ConstOp Support both signed - const int and unsigned const int - - -diff --git a/lib/Translate/GimpleToPluginOps.cpp b/lib/Translate/GimpleToPluginOps.cpp -index a5380a7..3a71ffe 100644 ---- a/lib/Translate/GimpleToPluginOps.cpp -+++ b/lib/Translate/GimpleToPluginOps.cpp -@@ -812,9 +812,16 @@ Value GimpleToPluginOps::TreeToValue(uint64_t treeId) - mlir::Value opValue; - switch (TREE_CODE(t)) { - case INTEGER_CST : { -- unsigned HOST_WIDE_INT init = tree_to_uhwi(t); -- // FIXME : AnyAttr! -- mlir::Attribute initAttr = builder.getI64IntegerAttr(init); -+ mlir::Attribute initAttr; -+ if (tree_fits_shwi_p(t)) { -+ signed HOST_WIDE_INT sinit = tree_to_shwi(t); -+ initAttr = builder.getI64IntegerAttr(sinit); -+ } else if (tree_fits_uhwi_p(t)) { -+ unsigned HOST_WIDE_INT uinit = tree_to_uhwi(t); -+ initAttr = builder.getI64IntegerAttr(uinit); -+ } else { -+ abort(); -+ } - opValue = builder.create( - builder.getUnknownLoc(), treeId, IDefineCode::IntCST, - readOnly, initAttr, rPluginType); --- -2.27.0.windows.1 - diff --git a/0002-Refactoring-Code-refactoring-of-Communication-Subsys.patch b/0002-Refactoring-Code-refactoring-of-Communication-Subsys.patch deleted file mode 100644 index ef0750f..0000000 --- a/0002-Refactoring-Code-refactoring-of-Communication-Subsys.patch +++ /dev/null @@ -1,2327 +0,0 @@ -From e5e442c4b900eff2023232b2000c7be4048c68cc Mon Sep 17 00:00:00 2001 -From: dingguangya -Date: Tue, 7 Feb 2023 19:11:26 +0800 -Subject: [PATCH 2/9] [Refactoring] Code refactoring of Communication Subsystem - [1/3]. Code refactoring of PluginClient. - - -diff --git a/lib/PluginClient/PluginClient.cpp b/lib/PluginClient/PluginClient.cpp -index de15140..18877c2 100644 ---- a/lib/PluginClient/PluginClient.cpp -+++ b/lib/PluginClient/PluginClient.cpp -@@ -19,27 +19,16 @@ - Description: - This file contains the implementation of the PluginClient class.. - */ -+#include -+#include "gccPlugin/gccPlugin.h" - #include "Dialect/PluginDialect.h" - #include "Dialect/PluginTypes.h" - #include "PluginAPI/PluginClientAPI.h" --#include "IRTrans/IRTransPlugin.h" -- --#include --#include --#include --#include --#include --#include --#include --#include - - namespace PinClient { - using namespace mlir::Plugin; - using namespace mlir; --using std::ios; --static std::shared_ptr g_plugin = nullptr; --const char *g_portFilePath = "/tmp/grpc_ports_pin_client.txt"; --static std::shared_ptr g_grpcChannel = nullptr; -+static PluginClient g_plugin; - - std::map g_injectPoint { - {HANDLE_PARSE_TYPE, PLUGIN_FINISH_TYPE}, -@@ -56,15 +45,9 @@ std::map g_injectPoint { - {HANDLE_MANAGER_SETUP, PLUGIN_PASS_MANAGER_SETUP}, - }; - --std::shared_ptr PluginClient::GetInstance(void) --{ -- return g_plugin; --} -- --static uintptr_t GetID(Json::Value node) -+PluginClient *PluginClient::GetInstance() - { -- string id = node.asString(); -- return atol(id.c_str()); -+ return &g_plugin; - } - - int PluginClient::GetEvent(InjectPoint inject, plugin_event *event) -@@ -77,1138 +60,861 @@ int PluginClient::GetEvent(InjectPoint inject, plugin_event *event) - return -1; - } - --Json::Value PluginClient::TypeJsonSerialize (PluginIR::PluginTypeBase& type) -+void PluginClient::GetGccData(const string& funcName, const string& param, string& key, string& result) - { -- Json::Value root; -- Json::Value operationObj; -- Json::Value item; -- -- uint64_t ReTypeId; -- uint64_t ReTypeWidth; -- -- ReTypeId = static_cast(type.getPluginTypeID()); -- item["id"] = std::to_string(ReTypeId); -- -- if (auto elemTy = type.dyn_cast()) { -- auto baseTy = elemTy.getElementType().dyn_cast(); -- item["elementType"] = TypeJsonSerialize(baseTy); -- if (elemTy.isReadOnlyElem()) { -- item["elemConst"] = "1"; -- }else { -- item["elemConst"] = "0"; -- } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI pluginAPI(context); -+ uint64_t gccDataAddr = (uint64_t)atol(param.c_str()); -+ if (gccDataAddr == 0) { -+ LOGE("%s gcc_data address is NULL!\n", __func__); -+ return; - } -- -- if (type.getPluginIntOrFloatBitWidth() != 0) { -- ReTypeWidth = type.getPluginIntOrFloatBitWidth(); -- item["width"] = std::to_string(ReTypeWidth); -+ if (funcName == "GetDeclSourceFile") { -+ string sourceFile = pluginAPI.GetDeclSourceFile(gccDataAddr); -+ json.StringSerialize(sourceFile, result); -+ key = "StringResult"; -+ } else if (funcName == "GetDeclSourceLine") { -+ int line = pluginAPI.GetDeclSourceLine(gccDataAddr); -+ json.IntegerSerialize(line, result); -+ key = "IntegerResult"; -+ } else if (funcName == "GetDeclSourceColumn") { -+ int column = pluginAPI.GetDeclSourceColumn(gccDataAddr); -+ json.IntegerSerialize(column, result); -+ key = "IntegerResult"; -+ } else if (funcName == "VariableName") { -+ string variableName = pluginAPI.VariableName(gccDataAddr); -+ json.StringSerialize(variableName, result); -+ key = "StringResult"; -+ } else if (funcName == "FuncName") { -+ string funcName = pluginAPI.FuncName(gccDataAddr); -+ json.StringSerialize(funcName, result); -+ key = "StringResult"; -+ } else { -+ LOGW("function: %s not found!\n", funcName.c_str()); - } -+} - -- if (type.isSignedPluginInteger()) { -- item["signed"] = "1"; -- } -+void GetAllFuncResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ // Load our Dialect in this MLIR Context. -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ vector allFuncOps = clientAPI.GetAllFunc(); -+ PluginJson json = client->GetJson(); -+ json.FunctionOpJsonSerialize(allFuncOps, result); -+ client->ReceiveSendMsg("FuncOpResult", result); -+} - -- if (type.isUnsignedPluginInteger()) { -- item["signed"] = "0"; -- } -+void GetLocalDeclsResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ /// Json格式 -+ /// { -+ /// "funcId":"xxxx" -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string funcIdKey = "funcId"; -+ uint64_t funcID = atol(root[funcIdKey].asString().c_str()); -+ vector decls = clientAPI.GetDecls(funcID); -+ PluginJson json = client->GetJson(); -+ json.LocalDeclsJsonSerialize(decls, result); -+ client->ReceiveSendMsg("LocalDeclOpResult", result); -+} - -- root["type"] = item; -- return root; -+void GetLoopsFromFuncResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ /// Json格式 -+ /// { -+ /// "funcId":"xxxx" -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string funcIdKey = "funcId"; -+ uint64_t funcID = atol(root[funcIdKey].asString().c_str()); -+ vector irLoops = clientAPI.GetLoopsFromFunc(funcID); -+ PluginJson json = client->GetJson(); -+ json.LoopOpsJsonSerialize(irLoops, result); -+ client->ReceiveSendMsg("LoopOpsResult", result); - } - --PluginIR::PluginTypeBase PluginClient::TypeJsonDeSerialize(const string& data, mlir::MLIRContext &context) -+void GetLoopByIdResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- Json::Reader reader; -- Json::Value node; -- reader.parse(data, root); -+ /// Json格式 -+ /// { -+ /// "loopId":"xxxx" -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string loopIdKey = "loopId"; -+ uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -+ LoopOp irLoop = clientAPI.GetLoopById(loopId); -+ PluginJson json = client->GetJson(); -+ json.LoopOpJsonSerialize(irLoop, result); -+ client->ReceiveSendMsg("LoopOpResult", result); -+} - -- PluginIR::PluginTypeBase baseType; -+void IsBlockInsideResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ /// Json格式 -+ /// { -+ /// "loopId":"xxxx", -+ /// "blockId":"xxxx" -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string loopIdKey = "loopId"; -+ std::string blockIdKey = "blockId"; -+ uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -+ uint64_t blockId = atol(root[blockIdKey].asString().c_str()); -+ bool res = clientAPI.IsBlockInside(loopId, blockId); -+ client->ReceiveSendMsg("BoolResult", std::to_string((uint64_t)res)); -+} - -- Json::Value type = root["type"]; -- uint64_t id = GetID(type["id"]); -- PluginIR::PluginTypeID PluginTypeId = static_cast(id); -+void AllocateNewLoopResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ uint64_t newLoopId = clientAPI.AllocateNewLoop(); -+ LoopOp newLoop = clientAPI.GetLoopById(newLoopId); -+ PluginJson json = client->GetJson(); -+ json.LoopOpJsonSerialize(newLoop, result); -+ client->ReceiveSendMsg("LoopOpResult", result); -+} - -- if (type["signed"] && (id >= static_cast(PluginIR::UIntegerTy1ID) -- && id <= static_cast(PluginIR::IntegerTy64ID))) { -- string s = type["signed"].asString(); -- uint64_t width = GetID(type["width"]); -- if (s == "1") { -- baseType = PluginIR::PluginIntegerType::get(&context, width, PluginIR::PluginIntegerType::Signed); -- } -- else { -- baseType = PluginIR::PluginIntegerType::get(&context, width, PluginIR::PluginIntegerType::Unsigned); -- } -- } -- else if (type["width"] && (id == static_cast(PluginIR::FloatTyID) -- || id == static_cast(PluginIR::DoubleTyID)) ) { -- uint64_t width = GetID(type["width"]); -- baseType = PluginIR::PluginFloatType::get(&context, width); -- }else if (id == static_cast(PluginIR::PointerTyID)) { -- mlir::Type elemTy = TypeJsonDeSerialize(type["elementType"].toStyledString(), context); -- baseType = PluginIR::PluginPointerType::get(&context, elemTy, type["elemConst"].asString() == "1" ? 1 : 0); -- }else { -- if (PluginTypeId == PluginIR::VoidTyID) -- baseType = PluginIR::PluginVoidType::get(&context); -- if (PluginTypeId == PluginIR::BooleanTyID) -- baseType = PluginIR::PluginBooleanType::get(&context); -- if (PluginTypeId == PluginIR::UndefTyID) -- baseType = PluginIR::PluginUndefType::get(&context); -- } -+void RedirectFallthroughTargetResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ /// Json格式 -+ /// { -+ /// "src":"xxxx", -+ /// "dest":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string srcKey = "src"; -+ uint64_t src = atol(root[srcKey].asString().c_str()); -+ std::string destKey = "dest"; -+ uint64_t dest = atol(root[destKey].asString().c_str()); -+ clientAPI.RedirectFallthroughTarget(src, dest); -+ PluginJson json = client->GetJson(); -+ json.NopJsonSerialize(result); -+ client->ReceiveSendMsg("VoidResult", result); -+} - -- return baseType; -+void DeleteLoopResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ /// Json格式 -+ /// { -+ /// "loopId":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string loopIdKey = "loopId"; -+ uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -+ clientAPI.DeleteLoop(loopId); -+ PluginJson json = client->GetJson(); -+ json.NopJsonSerialize(result); -+ client->ReceiveSendMsg("VoidResult", result); - } - --void PluginClient::FunctionOpJsonSerialize(vector& data, string& out) -+void AddBlockToLoopResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- Json::Value operationObj; -- Json::Value item; -+ /// Json格式 -+ /// { -+ /// "loopId":"xxxx", -+ /// "blockId":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string loopIdKey = "loopId"; -+ uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -+ std::string blockIdKey = "blockId"; -+ uint64_t blockId = atol(root[blockIdKey].asString().c_str()); -+ clientAPI.AddBlockToLoop(blockId, loopId); -+ PluginJson json = client->GetJson(); -+ json.NopJsonSerialize(result); -+ client->ReceiveSendMsg("VoidResult", result); -+} - -- int i = 0; -- string operation; -- -- for (auto& d: data) { -- item["id"] = std::to_string(d.idAttr().getInt()); -- // item["opCode"] = OP_FUNCTION; -- if (d.declaredInlineAttr().getValue()) -- item["attributes"]["declaredInline"] = "1"; -- else -- item["attributes"]["declaredInline"] = "0"; -- item["attributes"]["funcName"] = d.funcNameAttr().getValue().str().c_str(); -- auto ®ion = d.getRegion(); -- size_t bbIdx = 0; -- for (auto &b : region) { -- string blockStr = "block" + std::to_string(bbIdx++); -- uint64_t bbAddress = 0; -- size_t opIdx = 0; -- for (auto &inst : b) { -- if (isa(inst)) continue; -- else if (isa(inst)) continue; -- else if (isa(inst)) continue; -- else if (isa(inst)) continue; -- string opStr = "Operation" + std::to_string(opIdx++); -- item["region"][blockStr]["ops"][opStr] = OperationJsonSerialize(&inst, bbAddress); -- } -- assert(bbAddress != 0); -- item["region"][blockStr]["address"] = std::to_string(bbAddress); -- } -- operation = "FunctionOp" + std::to_string(i++); -- root[operation] = item; -- item.clear(); -- } -- out = root.toStyledString(); -+void AddLoopResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ /// Json格式 -+ /// { -+ /// "loopId":"xxxx", -+ /// "outerId":"xxxx" -+ /// "funcId":"xxxx" -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string loopIdKey = "loopId"; -+ std::string outerIdKey = "outerId"; -+ std::string funcIdKey = "funcId"; -+ uint64_t loopID = atol(root[loopIdKey].asString().c_str()); -+ uint64_t outerID = atol(root[outerIdKey].asString().c_str()); -+ uint64_t funcID = atol(root[funcIdKey].asString().c_str()); -+ clientAPI.AddLoop(loopID, outerID, funcID); -+ LoopOp irLoop = clientAPI.GetLoopById(loopID); -+ PluginJson json = client->GetJson(); -+ json.LoopOpJsonSerialize(irLoop, result); -+ client->ReceiveSendMsg("LoopOpResult", result); - } - --Json::Value PluginClient::OperationJsonSerialize(mlir::Operation *operation, -- uint64_t &bbId) -+void GetBlocksInLoopResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- if (AssignOp op = llvm::dyn_cast(operation)) { -- root = AssignOpJsonSerialize(op); -- } else if (CallOp op = llvm::dyn_cast(operation)) { -- root = CallOpJsonSerialize(op); -- } else if (CondOp op = llvm::dyn_cast(operation)) { -- root = CondOpJsonSerialize(op, bbId); -- } else if (PhiOp op = llvm::dyn_cast(operation)) { -- root = PhiOpJsonSerialize(op); -- } else if (FallThroughOp op = llvm::dyn_cast(operation)) { -- root = FallThroughOpJsonSerialize(op, bbId); -- } else if (RetOp op = llvm::dyn_cast(operation)) { -- root = RetOpJsonSerialize(op, bbId); -- } else if (BaseOp op = llvm::dyn_cast(operation)) { -- root = BaseOpJsonSerialize(op); -- } -- root["OperationName"] = operation->getName().getStringRef().str(); -- return root; -+ /// Json格式 -+ /// { -+ /// "loopId":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string loopIdKey = "loopId"; -+ uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -+ vector blocks = clientAPI.GetBlocksInLoop(loopId); -+ PluginJson json = client->GetJson(); -+ json.BlocksJsonSerialize(blocks, result); -+ client->ReceiveSendMsg("IdsResult", result); - } - --Json::Value PluginClient::BaseOpJsonSerialize(BaseOp data) -+void GetHeaderResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- root["id"] = std::to_string(data.idAttr().getInt()); -- root["opCode"] = data.opCodeAttr().getValue().str().c_str(); -- return root; -+ /// Json格式 -+ /// { -+ /// "loopId":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string loopIdKey = "loopId"; -+ uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -+ uint64_t blockId = clientAPI.GetHeader(loopId); -+ client->ReceiveSendMsg("IdResult", std::to_string(blockId)); - } - --Json::Value PluginClient::RetOpJsonSerialize(RetOp data, uint64_t &bbId) -+void GetLatchResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- bbId = data.addressAttr().getInt(); -- root["address"] = std::to_string(bbId); -- return root; -+ /// Json格式 -+ /// { -+ /// "loopId":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string loopIdKey = "loopId"; -+ uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -+ uint64_t blockId = clientAPI.GetLatch(loopId); -+ client->ReceiveSendMsg("IdResult", std::to_string(blockId)); - } - --Json::Value PluginClient::FallThroughOpJsonSerialize(FallThroughOp data, -- uint64_t &bbId) -+void SetHeaderResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- bbId = data.addressAttr().getInt(); -- root["address"] = std::to_string(bbId); -- root["destaddr"] = std::to_string(data.destaddrAttr().getInt()); -- return root; -+ /// Json格式 -+ /// { -+ /// "loopId":"xxxx", -+ /// "blockId":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string loopIdKey = "loopId"; -+ uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -+ std::string blockIdKey = "blockId"; -+ uint64_t blockId = atol(root[blockIdKey].asString().c_str()); -+ clientAPI.SetHeader(loopId, blockId); -+ PluginJson json = client->GetJson(); -+ json.NopJsonSerialize(result); -+ client->ReceiveSendMsg("VoidResult", result); - } - --void PluginClient::LocalDeclsJsonSerialize(vector& decls, string& out) -+void SetLatchResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- Json::Value operationObj; -- Json::Value item; -- int i = 0; -- string operation; -- -- for (auto& decl: decls) { -- item["id"] = std::to_string(decl.idAttr().getInt()); -- item["attributes"]["symName"] = decl.symNameAttr().getValue().str().c_str(); -- item["attributes"]["typeID"] = decl.typeIDAttr().getInt(); -- item["attributes"]["typeWidth"] = decl.typeWidthAttr().getInt(); -- operation = "localDecl" + std::to_string(i++); -- root[operation] = item; -- item.clear(); -- } -- out = root.toStyledString(); -+ /// Json格式 -+ /// { -+ /// "loopId":"xxxx", -+ /// "blockId":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string loopIdKey = "loopId"; -+ uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -+ std::string blockIdKey = "blockId"; -+ uint64_t blockId = atol(root[blockIdKey].asString().c_str()); -+ clientAPI.SetLatch(loopId, blockId); -+ PluginJson json = client->GetJson(); -+ json.NopJsonSerialize(result); -+ client->ReceiveSendMsg("VoidResult", result); - } - --void PluginClient::LoopOpsJsonSerialize(vector& loops, string& out) -+void GetLoopExitsResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- Json::Value operationObj; -- Json::Value item; -- int i = 0; -- string operation; -- -- for (auto&loop: loops) { -- item["id"] = std::to_string(loop.idAttr().getInt()); -- item["index"] = std::to_string(loop.indexAttr().getInt()); -- item["attributes"]["innerLoopId"] = std::to_string(loop.innerLoopIdAttr().getInt()); -- item["attributes"]["outerLoopId"] = std::to_string(loop.outerLoopIdAttr().getInt()); -- item["attributes"]["numBlock"] = std::to_string(loop.numBlockAttr().getInt()); -- operation = "loopOp" + std::to_string(i++); -- root[operation] = item; -- item.clear(); -- } -- out = root.toStyledString(); -+ /// Json格式 -+ /// { -+ /// "loopId":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string loopIdKey = "loopId"; -+ uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -+ vector > edges = clientAPI.GetLoopExits(loopId); -+ PluginJson json = client->GetJson(); -+ json.EdgesJsonSerialize(edges, result); -+ client->ReceiveSendMsg("EdgesResult", result); - } - --void PluginClient::LoopOpJsonSerialize(mlir::Plugin::LoopOp& loop, string& out) -+void GetLoopSingleExitResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- root["id"] = std::to_string(loop.idAttr().getInt()); -- root["index"] = std::to_string(loop.indexAttr().getInt()); -- root["attributes"]["innerLoopId"] = std::to_string(loop.innerLoopIdAttr().getInt()); -- root["attributes"]["outerLoopId"] = std::to_string(loop.outerLoopIdAttr().getInt()); -- root["attributes"]["numBlock"] = std::to_string(loop.numBlockAttr().getInt()); -- out = root.toStyledString(); -+ /// Json格式 -+ /// { -+ /// "loopId":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string loopIdKey = "loopId"; -+ uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -+ std::pair edge = clientAPI.GetLoopSingleExit(loopId); -+ PluginJson json = client->GetJson(); -+ json.EdgeJsonSerialize(edge, result); -+ client->ReceiveSendMsg("EdgeResult", result); - } - --void PluginClient::BlocksJsonSerialize(vector& blocks, string& out) -+void GetBlockLoopFatherResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- Json::Value item; -- int i = 0; -- string index; -+ /// Json格式 -+ /// { -+ /// "blockId":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string blockIdKey = "blockId"; -+ uint64_t blockId = atol(root[blockIdKey].asString().c_str()); -+ LoopOp loopFather = clientAPI.GetBlockLoopFather(blockId); -+ PluginJson json = client->GetJson(); -+ json.LoopOpJsonSerialize(loopFather, result); -+ client->ReceiveSendMsg("LoopOpResult", result); -+} - -- for(auto& block : blocks) { -- item["id"] = std::to_string(block); -- index = "block" + std::to_string(i++); -- root[index] = item; -- item.clear(); -- } -- out = root.toStyledString(); -+void CreateBlockResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ /// Json格式 -+ /// { -+ /// "bbaddr":"xxxx", -+ /// "funcaddr":"xxxx" -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ uint64_t blockAddr = atol(root["bbaddr"].asString().c_str()); -+ uint64_t funcAddr = atol(root["funcaddr"].asString().c_str()); -+ uint64_t newBBAddr = clientAPI.CreateBlock(funcAddr, blockAddr); -+ client->ReceiveSendMsg("IdResult", std::to_string(newBBAddr)); - } - --void PluginClient::EdgesJsonSerialize(vector >& edges, string& out) -+void DeleteBlockResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- Json::Value item; -- int i = 0; -- string index; -- -- for(auto& edge : edges) { -- item["src"] = std::to_string(edge.first); -- item["dest"] = std::to_string(edge.second); -- index = "edge" + std::to_string(i++); -- root[index] = item; -- item.clear(); -- } -- out = root.toStyledString(); -+ /// Json格式 -+ /// { -+ /// "bbaddr":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string funcKey = "funcaddr"; -+ std::string BlockIdKey = "bbaddr"; -+ uint64_t bbaddr = atol(root[BlockIdKey].asString().c_str()); -+ uint64_t funcaddr = atol(root[funcKey].asString().c_str()); -+ clientAPI.DeleteBlock(funcaddr, bbaddr); -+ PluginJson json = client->GetJson(); -+ json.NopJsonSerialize(result); -+ client->ReceiveSendMsg("VoidResult", result); - } - --void PluginClient::EdgeJsonSerialize(pair& edge, string& out) -+void SetImmediateDominatorResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- root["src"] = std::to_string(edge.first); -- root["dest"] = std::to_string(edge.second); -- out = root.toStyledString(); -+ /// Json格式 -+ /// { -+ /// "dir":"xxxx", -+ /// "bbaddr":"xxxx", -+ /// "domiaddr":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string dirIdKey = "dir"; -+ uint64_t dir = atol(root[dirIdKey].asString().c_str()); -+ std::string BlockIdKey = "bbaddr"; -+ uint64_t bbaddr = atol(root[BlockIdKey].asString().c_str()); -+ std::string domiIdKey = "domiaddr"; -+ uint64_t domiaddr = atol(root[domiIdKey].asString().c_str()); -+ clientAPI.SetImmediateDominator(dir, bbaddr, domiaddr); -+ PluginJson json = client->GetJson(); -+ json.NopJsonSerialize(result); -+ client->ReceiveSendMsg("VoidResult", result); - } - --// void类型的Json序列化 --void PluginClient::NopJsonSerialize(string& out) -+void GetImmediateDominatorResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- out = root.toStyledString(); -+ /// Json格式 -+ /// { -+ /// "dir":"xxxx", -+ /// "bbaddr":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string dirIdKey = "dir"; -+ uint64_t dir = atol(root[dirIdKey].asString().c_str()); -+ std::string BlockIdKey = "bbaddr"; -+ uint64_t bbaddr = atol(root[BlockIdKey].asString().c_str()); -+ uint64_t ret = clientAPI.GetImmediateDominator(dir, bbaddr); -+ client->ReceiveSendMsg("IdResult", std::to_string(ret)); - } - --void PluginClient::GetPhiOpsJsonSerialize(vector phiOps, string & out) -+void RecomputeDominatorResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- Json::Value item; -- int i = 0; -- string operation; -- uint64_t placeholder = 0; -- for (auto phi : phiOps) { -- item = OperationJsonSerialize(phi.getOperation(), placeholder); -- operation = "operation" + std::to_string(i++); -- root[operation] = item; -- item.clear(); -- } -- out = root.toStyledString(); -+ /// Json格式 -+ /// { -+ /// "dir":"xxxx", -+ /// "bbaddr":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string dirIdKey = "dir"; -+ uint64_t dir = atol(root[dirIdKey].asString().c_str()); -+ std::string BlockIdKey = "bbaddr"; -+ uint64_t bbaddr = atol(root[BlockIdKey].asString().c_str()); -+ uint64_t ret = clientAPI.RecomputeDominator(dir, bbaddr); -+ client->ReceiveSendMsg("IdResult", std::to_string(ret)); - } - --Json::Value PluginClient::CallOpJsonSerialize(CallOp& data) -+void GetPhiOpResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value item; -- item["id"] = std::to_string(data.idAttr().getInt()); -- item["callee"] = data.callee().str(); -- item["OperationName"] = data.getOperation()->getName().getStringRef().str(); -- size_t opIdx = 0; -- for (mlir::Value v : data.getArgOperands()) { -- string input = "input" + std::to_string(opIdx++); -- item["operands"][input] = ValueJsonSerialize(v); -- } -- // auto retTy = data.getResultType().dyn_cast(); -- // item["retType"] = TypeJsonSerialize(retTy); -- return item; -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ uint64_t id = atol(root[std::to_string(0)].asString().c_str()); -+ PhiOp op = clientAPI.GetPhiOp(id); -+ PluginJson json = client->GetJson(); -+ Json::Value phiOpResult = json.PhiOpJsonSerialize(op); -+ client->ReceiveSendMsg("OpsResult", phiOpResult.toStyledString()); - } - --Json::Value PluginClient::CondOpJsonSerialize(CondOp& data, uint64_t &bbId) -+void GetCallOpResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value item; -- item["id"] = std::to_string(data.idAttr().getInt()); -- item["condCode"] = std::to_string(data.condCodeAttr().getInt()); -- item["lhs"] = ValueJsonSerialize(data.GetLHS()); -- item["rhs"] = ValueJsonSerialize(data.GetRHS()); -- bbId = data.addressAttr().getInt(); -- item["address"] = std::to_string(bbId); -- item["tbaddr"] = std::to_string(data.tbaddrAttr().getInt()); -- item["fbaddr"] = std::to_string(data.fbaddrAttr().getInt()); -- return item; -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ uint64_t id = atol(root[std::to_string(0)].asString().c_str()); -+ CallOp op = clientAPI.GetCallOp(id); -+ PluginJson json = client->GetJson(); -+ Json::Value opResult = json.CallOpJsonSerialize(op); -+ client->ReceiveSendMsg("OpsResult", opResult.toStyledString()); - } - --Json::Value PluginClient::PhiOpJsonSerialize(PhiOp& data) -+void SetLhsInCallOpResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value item; -- item["id"] = std::to_string(data.idAttr().getInt()); -- item["capacity"] = std::to_string(data.capacityAttr().getInt()); -- item["nArgs"] = std::to_string(data.nArgsAttr().getInt()); -- item["OperationName"] = data.getOperation()->getName().getStringRef().str(); -- size_t opIdx = 0; -- for (mlir::Value v : data.operands()) { -- string input = "input" + std::to_string(opIdx++); -- item["operands"][input] = ValueJsonSerialize(v); -- } -- // auto retTy = data.getResultType().dyn_cast(); -- // item["retType"] = TypeJsonSerialize(retTy); -- return item; -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ uint64_t callId = atol(root["callId"].asString().c_str()); -+ uint64_t lhsId = atol(root["lhsId"].asString().c_str()); -+ bool ret = clientAPI.SetLhsInCallOp(callId, lhsId); -+ client->ReceiveSendMsg("BoolResult", std::to_string(ret)); - } - --Json::Value PluginClient::SSAOpJsonSerialize(SSAOp& data) -+void AddArgInPhiOpResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value item; -- item["id"] = std::to_string(data.idAttr().getInt()); -- item["defCode"] = std::to_string(data.defCodeAttr().getInt()); -- item["readOnly"] = std::to_string(data.readOnlyAttr().getValue()); -- item["nameVarId"] = std::to_string(data.nameVarIdAttr().getInt()); -- item["ssaParmDecl"] = std::to_string(data.ssaParmDeclAttr().getInt()); -- item["version"] = std::to_string(data.versionAttr().getInt()); -- item["definingId"] = std::to_string(data.definingIdAttr().getInt()); -- auto retTy = data.getResultType().dyn_cast(); -- item["retType"] = TypeJsonSerialize(retTy); -- return item; -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ uint64_t phiId = atol(root["phiId"].asString().c_str()); -+ uint64_t argId = atol(root["argId"].asString().c_str()); -+ uint64_t predId = atol(root["predId"].asString().c_str()); -+ uint64_t succId = atol(root["succId"].asString().c_str()); -+ uint32_t ret = clientAPI.AddArgInPhiOp(phiId, argId, predId, succId); -+ client->ReceiveSendMsg("IdResult", std::to_string(ret)); - } - --Json::Value PluginClient::AssignOpJsonSerialize(AssignOp& data) -+void CreateCallOpResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value item; -- item["id"] = std::to_string(data.idAttr().getInt()); -- item["exprCode"] = std::to_string(data.exprCodeAttr().getInt()); -- item["OperationName"] = data.getOperation()->getName().getStringRef().str(); -- size_t opIdx = 0; -- for (mlir::Value v : data.operands()) { -- string input = "input" + std::to_string(opIdx++); -- item["operands"][input] = ValueJsonSerialize(v); -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ uint64_t blockId = atol(root["blockId"].asString().c_str()); -+ uint64_t funcId = atol(root["funcId"].asString().c_str()); -+ vector argIds; -+ Json::Value argIdsJson = root["argIds"]; -+ Json::Value::Members member = argIdsJson.getMemberNames(); -+ for (Json::Value::Members::iterator opIter = member.begin(); opIter != member.end(); opIter++) { -+ string key = *opIter; -+ uint64_t id = atol(argIdsJson[key.c_str()].asString().c_str()); -+ argIds.push_back(id); - } -- // auto retTy = data.getResultType().dyn_cast(); -- // item["retType"] = TypeJsonSerialize(retTy); -- return item; -+ uint64_t ret = clientAPI.CreateCallOp(blockId, funcId, argIds); -+ client->ReceiveSendMsg("IdResult", std::to_string(ret)); - } - --Json::Value PluginClient::ValueJsonSerialize(mlir::Value data) -+void CreateAssignOpResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- if (ConstOp cOp = data.getDefiningOp()) { -- auto retTy = data.getType().dyn_cast(); -- root["retType"] = TypeJsonSerialize(retTy); -- root["id"] = std::to_string(cOp.idAttr().getInt()); -- root["defCode"] = std::to_string( -- static_cast(IDefineCode::IntCST)); -- root["value"] = std::to_string( -- cOp.initAttr().cast().getInt()); -- } else if (MemOp mOp = data.getDefiningOp()) { -- root = MemOpJsonSerialize(mOp); -- } else if (SSAOp sOp = data.getDefiningOp()) { -- root = SSAOpJsonSerialize(sOp); -- } else if (PlaceholderOp phOp = data.getDefiningOp()) { -- root["id"] = std::to_string(phOp.idAttr().getInt()); -- root["defCode"] = std::to_string(phOp.defCodeAttr().getInt()); -- auto retTy = phOp.getResultType().dyn_cast(); -- root["retType"] = TypeJsonSerialize(retTy); -- } else { -- LOGE("ERROR: Can't Serialize!"); -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ uint64_t blockId = atol(root["blockId"].asString().c_str()); -+ int condCode = atol(root["exprCode"].asString().c_str()); -+ vector argIds; -+ Json::Value argIdsJson = root["argIds"]; -+ Json::Value::Members member = argIdsJson.getMemberNames(); -+ for (Json::Value::Members::iterator opIter = member.begin(); opIter != member.end(); opIter++) { -+ string key = *opIter; -+ uint64_t id = atol(argIdsJson[key.c_str()].asString().c_str()); -+ argIds.push_back(id); - } -- return root; -+ uint64_t ret = clientAPI.CreateAssignOp(blockId, IExprCode(condCode), argIds); -+ client->ReceiveSendMsg("IdResult", std::to_string(ret)); - } - --Json::Value PluginClient::MemOpJsonSerialize(MemOp& data) -+void CreateCondOpResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- root["id"] = std::to_string(data.idAttr().getInt()); -- root["defCode"] = std::to_string(data.defCodeAttr().getInt()); -- root["readOnly"] = std::to_string(data.readOnlyAttr().getValue()); -- mlir::Value base = data.GetBase(); -- mlir::Value offset = data.GetOffset(); -- root["base"] = ValueJsonSerialize(base); -- root["offset"] = ValueJsonSerialize(offset); -- auto retTy = data.getResultType().dyn_cast(); -- root["retType"] = TypeJsonSerialize(retTy); -- return root; -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ uint64_t blockId = atol(root["blockId"].asString().c_str()); -+ int condCode = atol(root["condCode"].asString().c_str()); -+ uint64_t lhsId = atol(root["lhsId"].asString().c_str()); -+ uint64_t rhsId = atol(root["rhsId"].asString().c_str()); -+ uint64_t tbaddr = atol(root["tbaddr"].asString().c_str()); -+ uint64_t fbaddr = atol(root["fbaddr"].asString().c_str()); -+ uint64_t ret = clientAPI.CreateCondOp(blockId, IComparisonCode(condCode), lhsId, rhsId, tbaddr, fbaddr); -+ client->ReceiveSendMsg("IdResult", std::to_string(ret)); - } - --void PluginClient::IRTransBegin(const string& funcName, const string& param) -+void CreateFallthroughOpResult(PluginClient *client, Json::Value& root, string& result) - { -- string result; -- -- Json::Value root; -- Json::Reader reader; -- reader.parse(param, root); -- LOGD("%s func:%s,param:%s\n", __func__, funcName.c_str(), param.c_str()); -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ uint64_t address = atol(root["address"].asString().c_str()); -+ uint64_t destaddr = atol(root["destaddr"].asString().c_str()); -+ clientAPI.CreateFallthroughOp(address, destaddr); -+ PluginJson json = client->GetJson(); -+ json.NopJsonSerialize(result); -+ client->ReceiveSendMsg("VoidResult", result); -+} - -- if (funcName == "GetAllFunc") { -- // Load our Dialect in this MLIR Context. -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- vector allFuncOps = clientAPI.GetAllFunc(); -- FunctionOpJsonSerialize(allFuncOps, result); -- this->ReceiveSendMsg("FuncOpResult", result); -- } else if (funcName == "GetLocalDecls") { -- /// Json格式 -- /// { -- /// "funcId":"xxxx" -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string funcIdKey = "funcId"; -- uint64_t funcID = atol(root[funcIdKey].asString().c_str()); -- vector decls = clientAPI.GetDecls(funcID); -- LocalDeclsJsonSerialize(decls, result); -- this->ReceiveSendMsg("LocalDeclOpResult", result); -- } else if (funcName == "GetLoopsFromFunc") { -- /// Json格式 -- /// { -- /// "funcId":"xxxx" -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string funcIdKey = "funcId"; -- uint64_t funcID = atol(root[funcIdKey].asString().c_str()); -- vector irLoops = clientAPI.GetLoopsFromFunc(funcID); -- LoopOpsJsonSerialize(irLoops, result); -- this->ReceiveSendMsg("LoopOpsResult", result); -- } else if (funcName == "GetLoopById") { -- /// Json格式 -- /// { -- /// "loopId":"xxxx" -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string loopIdKey = "loopId"; -- uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -- LoopOp irLoop = clientAPI.GetLoopById(loopId); -- LoopOpJsonSerialize(irLoop, result); -- this->ReceiveSendMsg("LoopOpResult", result); -- } else if (funcName == "IsBlockInside") { -- /// Json格式 -- /// { -- /// "loopId":"xxxx", -- /// "blockId":"xxxx" -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string loopIdKey = "loopId"; -- std::string blockIdKey = "blockId"; -- uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -- uint64_t blockId = atol(root[blockIdKey].asString().c_str()); -- bool res = clientAPI.IsBlockInside(loopId, blockId); -- this->ReceiveSendMsg("BoolResult", std::to_string((uint64_t)res)); -- } else if (funcName == "AllocateNewLoop") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- uint64_t newLoopId = clientAPI.AllocateNewLoop(); -- LoopOp newLoop = clientAPI.GetLoopById(newLoopId); -- LoopOpJsonSerialize(newLoop, result); -- this->ReceiveSendMsg("LoopOpResult", result); -- } else if (funcName == "RedirectFallthroughTarget") { -- /// Json格式 -- /// { -- /// "src":"xxxx", -- /// "dest":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string srcKey = "src"; -- uint64_t src = atol(root[srcKey].asString().c_str()); -- std::string destKey = "dest"; -- uint64_t dest = atol(root[destKey].asString().c_str()); -- clientAPI.RedirectFallthroughTarget(src, dest); -- NopJsonSerialize(result); -- this->ReceiveSendMsg("VoidResult", result); -- } else if (funcName == "DeleteLoop") { -- /// Json格式 -- /// { -- /// "loopId":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string loopIdKey = "loopId"; -- uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -- clientAPI.DeleteLoop(loopId); -- NopJsonSerialize(result); -- this->ReceiveSendMsg("VoidResult", result); -- } else if (funcName == "AddBlockToLoop") { -- /// Json格式 -- /// { -- /// "loopId":"xxxx", -- /// "blockId":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string loopIdKey = "loopId"; -- uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -- std::string blockIdKey = "blockId"; -- uint64_t blockId = atol(root[blockIdKey].asString().c_str()); -- clientAPI.AddBlockToLoop(blockId, loopId); -- NopJsonSerialize(result); -- this->ReceiveSendMsg("VoidResult", result); -- } else if (funcName == "AddLoop") { -- /// Json格式 -- /// { -- /// "loopId":"xxxx", -- /// "outerId":"xxxx" -- /// "funcId":"xxxx" -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string loopIdKey = "loopId"; -- std::string outerIdKey = "outerId"; -- std::string funcIdKey = "funcId"; -- uint64_t loopID = atol(root[loopIdKey].asString().c_str()); -- uint64_t outerID = atol(root[outerIdKey].asString().c_str()); -- uint64_t funcID = atol(root[funcIdKey].asString().c_str()); -- clientAPI.AddLoop(loopID, outerID, funcID); -- LoopOp irLoop = clientAPI.GetLoopById(loopID); -- LoopOpJsonSerialize(irLoop, result); -- this->ReceiveSendMsg("LoopOpResult", result); -- } else if (funcName == "GetBlocksInLoop") { -- /// Json格式 -- /// { -- /// "loopId":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string loopIdKey = "loopId"; -- uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -- vector blocks = clientAPI.GetBlocksInLoop(loopId); -- BlocksJsonSerialize(blocks, result); -- this->ReceiveSendMsg("IdsResult", result); -- } else if (funcName == "GetHeader") { -- /// Json格式 -- /// { -- /// "loopId":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string loopIdKey = "loopId"; -- uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -- uint64_t blockId = clientAPI.GetHeader(loopId); -- this->ReceiveSendMsg("IdResult", std::to_string(blockId)); -- } else if (funcName == "GetLatch") { -- /// Json格式 -- /// { -- /// "loopId":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string loopIdKey = "loopId"; -- uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -- uint64_t blockId = clientAPI.GetLatch(loopId); -- this->ReceiveSendMsg("IdResult", std::to_string(blockId)); -- } else if (funcName == "SetHeader") { -- /// Json格式 -- /// { -- /// "loopId":"xxxx", -- /// "blockId":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string loopIdKey = "loopId"; -- uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -- std::string blockIdKey = "blockId"; -- uint64_t blockId = atol(root[blockIdKey].asString().c_str()); -- clientAPI.SetHeader(loopId, blockId); -- NopJsonSerialize(result); -- this->ReceiveSendMsg("VoidResult", result); -- } else if (funcName == "SetLatch") { -- /// Json格式 -- /// { -- /// "loopId":"xxxx", -- /// "blockId":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string loopIdKey = "loopId"; -- uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -- std::string blockIdKey = "blockId"; -- uint64_t blockId = atol(root[blockIdKey].asString().c_str()); -- clientAPI.SetLatch(loopId, blockId); -- NopJsonSerialize(result); -- this->ReceiveSendMsg("VoidResult", result); -- } else if (funcName == "GetLoopExits") { -- /// Json格式 -- /// { -- /// "loopId":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string loopIdKey = "loopId"; -- uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -- vector > edges = clientAPI.GetLoopExits(loopId); -- EdgesJsonSerialize(edges, result); -- this->ReceiveSendMsg("EdgesResult", result); -- } else if (funcName == "GetLoopSingleExit") { -- /// Json格式 -- /// { -- /// "loopId":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string loopIdKey = "loopId"; -- uint64_t loopId = atol(root[loopIdKey].asString().c_str()); -- pair edge = clientAPI.GetLoopSingleExit(loopId); -- EdgeJsonSerialize(edge, result); -- this->ReceiveSendMsg("EdgeResult", result); -- } else if (funcName == "GetBlockLoopFather") { -- /// Json格式 -- /// { -- /// "blockId":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string blockIdKey = "blockId"; -- uint64_t blockId = atol(root[blockIdKey].asString().c_str()); -- LoopOp loopFather = clientAPI.GetBlockLoopFather(blockId); -- LoopOpJsonSerialize(loopFather, result); -- this->ReceiveSendMsg("LoopOpResult", result); -- } else if (funcName == "CreateBlock") { -- /// Json格式 -- /// { -- /// "bbaddr":"xxxx", -- /// "funcaddr":"xxxx" -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- uint64_t blockAddr = atol(root["bbaddr"].asString().c_str()); -- uint64_t funcAddr = atol(root["funcaddr"].asString().c_str()); -- uint64_t newBBAddr = clientAPI.CreateBlock(funcAddr, blockAddr); -- this->ReceiveSendMsg("IdResult", std::to_string(newBBAddr)); -- } else if (funcName == "DeleteBlock") { -- /// Json格式 -- /// { -- /// "bbaddr":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string funcKey = "funcaddr"; -- std::string BlockIdKey = "bbaddr"; -- uint64_t bbaddr = atol(root[BlockIdKey].asString().c_str()); -- uint64_t funcaddr = atol(root[funcKey].asString().c_str()); -- clientAPI.DeleteBlock(funcaddr, bbaddr); -- NopJsonSerialize(result); -- this->ReceiveSendMsg("VoidResult", result); -- } else if (funcName == "SetImmediateDominator") { -- /// Json格式 -- /// { -- /// "dir":"xxxx", -- /// "bbaddr":"xxxx", -- /// "domiaddr":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string dirIdKey = "dir"; -- uint64_t dir = atol(root[dirIdKey].asString().c_str()); -- std::string BlockIdKey = "bbaddr"; -- uint64_t bbaddr = atol(root[BlockIdKey].asString().c_str()); -- std::string domiIdKey = "domiaddr"; -- uint64_t domiaddr = atol(root[domiIdKey].asString().c_str()); -- clientAPI.SetImmediateDominator(dir, bbaddr, domiaddr); -- NopJsonSerialize(result); -- this->ReceiveSendMsg("VoidResult", result); -- } else if (funcName == "GetImmediateDominator") { -- /// Json格式 -- /// { -- /// "dir":"xxxx", -- /// "bbaddr":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string dirIdKey = "dir"; -- uint64_t dir = atol(root[dirIdKey].asString().c_str()); -- std::string BlockIdKey = "bbaddr"; -- uint64_t bbaddr = atol(root[BlockIdKey].asString().c_str()); -- uint64_t ret = clientAPI.GetImmediateDominator(dir, bbaddr); -- this->ReceiveSendMsg("IdResult", std::to_string(ret)); -- } else if (funcName == "RecomputeDominator") { -- /// Json格式 -- /// { -- /// "dir":"xxxx", -- /// "bbaddr":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string dirIdKey = "dir"; -- uint64_t dir = atol(root[dirIdKey].asString().c_str()); -- std::string BlockIdKey = "bbaddr"; -- uint64_t bbaddr = atol(root[BlockIdKey].asString().c_str()); -- uint64_t ret = clientAPI.RecomputeDominator(dir, bbaddr); -- this->ReceiveSendMsg("IdResult", std::to_string(ret)); -- } else if (funcName == "GetPhiOp") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- uint64_t id = atol(root[std::to_string(0)].asString().c_str()); -- PhiOp op = clientAPI.GetPhiOp(id); -- Json::Value result = PhiOpJsonSerialize(op); -- this->ReceiveSendMsg("OpsResult", result.toStyledString()); -- } else if (funcName == "GetCallOp") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- uint64_t id = atol(root[std::to_string(0)].asString().c_str()); -- CallOp op = clientAPI.GetCallOp(id); -- Json::Value result = CallOpJsonSerialize(op); -- this->ReceiveSendMsg("OpsResult", result.toStyledString()); -- } else if (funcName == "SetLhsInCallOp") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- uint64_t callId = atol(root["callId"].asString().c_str()); -- uint64_t lhsId = atol(root["lhsId"].asString().c_str()); -- bool ret = clientAPI.SetLhsInCallOp(callId, lhsId); -- this->ReceiveSendMsg("BoolResult", std::to_string(ret)); -- } else if (funcName == "AddArgInPhiOp") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- uint64_t phiId = atol(root["phiId"].asString().c_str()); -- uint64_t argId = atol(root["argId"].asString().c_str()); -- uint64_t predId = atol(root["predId"].asString().c_str()); -- uint64_t succId = atol(root["succId"].asString().c_str()); -- uint32_t ret = clientAPI.AddArgInPhiOp(phiId, argId, predId, succId); -- this->ReceiveSendMsg("IdResult", std::to_string(ret)); -- } else if (funcName == "CreateCallOp") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- uint64_t blockId = atol(root["blockId"].asString().c_str()); -- uint64_t funcId = atol(root["funcId"].asString().c_str()); -- vector argIds; -- Json::Value argIdsJson = root["argIds"]; -- Json::Value::Members member = argIdsJson.getMemberNames(); -- for (Json::Value::Members::iterator opIter = member.begin(); -- opIter != member.end(); opIter++) { -- string key = *opIter; -- uint64_t id = atol(argIdsJson[key.c_str()].asString().c_str()); -- argIds.push_back(id); -- } -- uint64_t ret = clientAPI.CreateCallOp(blockId, funcId, argIds); -- this->ReceiveSendMsg("IdResult", std::to_string(ret)); -- } else if (funcName == "CreateAssignOp") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- uint64_t blockId = atol(root["blockId"].asString().c_str()); -- int condCode = atol(root["exprCode"].asString().c_str()); -- vector argIds; -- Json::Value argIdsJson = root["argIds"]; -- Json::Value::Members member = argIdsJson.getMemberNames(); -- for (Json::Value::Members::iterator opIter = member.begin(); -- opIter != member.end(); opIter++) { -- string key = *opIter; -- uint64_t id = atol(argIdsJson[key.c_str()].asString().c_str()); -- argIds.push_back(id); -- } -- uint64_t ret = clientAPI.CreateAssignOp( -- blockId, IExprCode(condCode), argIds); -- this->ReceiveSendMsg("IdResult", std::to_string(ret)); -- } else if (funcName == "CreateCondOp") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- uint64_t blockId = atol(root["blockId"].asString().c_str()); -- int condCode = atol(root["condCode"].asString().c_str()); -- uint64_t lhsId = atol(root["lhsId"].asString().c_str()); -- uint64_t rhsId = atol(root["rhsId"].asString().c_str()); -- uint64_t tbaddr = atol(root["tbaddr"].asString().c_str()); -- uint64_t fbaddr = atol(root["fbaddr"].asString().c_str()); -- uint64_t ret = clientAPI.CreateCondOp(blockId, IComparisonCode(condCode), -- lhsId, rhsId, tbaddr, fbaddr); -- this->ReceiveSendMsg("IdResult", std::to_string(ret)); -- } else if (funcName == "CreateFallthroughOp") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- uint64_t address = atol(root["address"].asString().c_str()); -- uint64_t destaddr = atol(root["destaddr"].asString().c_str()); -- clientAPI.CreateFallthroughOp(address, destaddr); -- NopJsonSerialize(result); -- this->ReceiveSendMsg("VoidResult", result); -- } else if (funcName == "GetResultFromPhi") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- uint64_t id = atol(root["id"].asString().c_str()); -- mlir::Value ret = clientAPI.GetResultFromPhi(id); -- this->ReceiveSendMsg("ValueResult", -- ValueJsonSerialize(ret).toStyledString()); -- } else if (funcName == "CreatePhiOp") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- uint64_t argId = atol(root["argId"].asString().c_str()); -- uint64_t blockId = atol(root["blockId"].asString().c_str()); -- PhiOp op = clientAPI.CreatePhiOp(argId, blockId); -- Json::Value result = PhiOpJsonSerialize(op); -- this->ReceiveSendMsg("OpsResult", result.toStyledString()); -- } else if (funcName == "CreateConstOp") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- PluginIR::PluginTypeBase type = TypeJsonDeSerialize(root.toStyledString(), context); -- uint64_t value = atol(root["value"].asString().c_str()); -- mlir::OpBuilder opBuilder = mlir::OpBuilder(&context); -- mlir::Attribute attr = opBuilder.getI64IntegerAttr(value); -- mlir::Value ret = clientAPI.CreateConstOp(attr, type); -- this->ReceiveSendMsg("ValueResult", -- ValueJsonSerialize(ret).toStyledString()); -- } else if (funcName == "UpdateSSA") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- bool ret = clientAPI.UpdateSSA(); -- this->ReceiveSendMsg("BoolResult", std::to_string((uint64_t)ret)); -- } else if (funcName == "GetAllPhiOpInsideBlock") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- uint64_t bb = atol(root["bbAddr"].asString().c_str()); -- vector phiOps = clientAPI.GetPhiOpsInsideBlock(bb); -- GetPhiOpsJsonSerialize(phiOps, result); -- this->ReceiveSendMsg("GetPhiOps", result); -- } else if (funcName == "IsDomInfoAvailable") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- bool ret = clientAPI.IsDomInfoAvailable(); -- this->ReceiveSendMsg("BoolResult", std::to_string((uint64_t)ret)); -- } else if (funcName == "GetCurrentDefFromSSA") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- uint64_t varId = atol(root["varId"].asString().c_str()); -- PluginAPI::PluginClientAPI clientAPI(context); -- mlir::Value ret = clientAPI.GetCurrentDefFromSSA(varId); -- this->ReceiveSendMsg("ValueResult", -- ValueJsonSerialize(ret).toStyledString()); -- } else if (funcName == "SetCurrentDefInSSA") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- uint64_t varId = atol(root["varId"].asString().c_str()); -- uint64_t defId = atol(root["defId"].asString().c_str()); -- PluginAPI::PluginClientAPI clientAPI(context); -- bool ret = clientAPI.SetCurrentDefInSSA(varId, defId); -- this->ReceiveSendMsg("BoolResult", std::to_string((uint64_t)ret)); -- } else if (funcName == "CopySSAOp") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- uint64_t id = atol(root["id"].asString().c_str()); -- mlir::Value ret = clientAPI.CopySSAOp(id); -- this->ReceiveSendMsg("ValueResult", -- ValueJsonSerialize(ret).toStyledString()); -- } else if (funcName == "CreateSSAOp") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- PluginIR::PluginTypeBase type = TypeJsonDeSerialize(root.toStyledString(), context); -- mlir::Value ret = clientAPI.CreateSSAOp(type); -- this->ReceiveSendMsg("ValueResult", -- ValueJsonSerialize(ret).toStyledString()); -- } else if (funcName == "CreateNewDef") { -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- uint64_t opId = atol(root["opId"].asString().c_str()); -- uint64_t valueId = atol(root["valueId"].asString().c_str()); -- uint64_t defId = atol(root["defId"].asString().c_str()); -- mlir::Value ret = clientAPI.CreateNewDef(valueId, opId, defId); -- this->ReceiveSendMsg("ValueResult", -- ValueJsonSerialize(ret).toStyledString()); -- } else if (funcName == "RemoveEdge") { -- /// Json格式 -- /// { -- /// "src":"xxxx", -- /// "dest":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string srcKey = "src"; -- uint64_t src = atol(root[srcKey].asString().c_str()); -- std::string destKey = "dest"; -- uint64_t dest = atol(root[destKey].asString().c_str()); -- clientAPI.RemoveEdge(src, dest); -- NopJsonSerialize(result); -- this->ReceiveSendMsg("VoidResult", result); -- } else if (funcName == "ConfirmValue") { -- /// Json格式 -- /// { -- /// "valId":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string valIdKey = "valId"; -- uint64_t valId = atol(root[valIdKey].asString().c_str()); -- mlir::Value v = clientAPI.GetValue(valId); -- Json::Value valueJson = ValueJsonSerialize(v); -- result = valueJson.toStyledString(); -- this->ReceiveSendMsg("ValueResult", result); -- } else if (funcName == "BuildMemRef") { -- /// Json格式 -- /// { -- /// "baseId":"xxxx", -- /// "offsetId":"xxxx", -- /// "type":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string baseIdKey = "baseId"; -- std::string offsetIdKey = "offsetId"; -- std::string typeKey = "type"; -- uint64_t baseId = atol(root[baseIdKey].asString().c_str()); -- uint64_t offsetId = atol(root[offsetIdKey].asString().c_str()); -- Json::Value type = root[typeKey]; -- PluginIR::PluginTypeBase pType = TypeJsonDeSerialize(type.toStyledString(), context); -- mlir::Value v = clientAPI.BuildMemRef(pType, baseId, offsetId); -- Json::Value valueJson = ValueJsonSerialize(v); -- result = valueJson.toStyledString(); -- this->ReceiveSendMsg("ValueResult", result); -- } else if (funcName == "DebugValue") { -- /// Json格式 -- /// { -- /// "valId":"xxxx", -- /// } -- mlir::MLIRContext context; -- context.getOrLoadDialect(); -- PluginAPI::PluginClientAPI clientAPI(context); -- std::string valIdKey = "valId"; -- uint64_t valId = atol(root[valIdKey].asString().c_str()); -- clientAPI.DebugValue(valId); -- NopJsonSerialize(result); -- this->ReceiveSendMsg("ValueResult", result); -- } else { -- LOGW("function: %s not found!\n", funcName.c_str()); -- } -+void GetResultFromPhiResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ uint64_t id = atol(root["id"].asString().c_str()); -+ mlir::Value ret = clientAPI.GetResultFromPhi(id); -+ PluginJson json = client->GetJson(); -+ client->ReceiveSendMsg("ValueResult", json.ValueJsonSerialize(ret).toStyledString()); -+} - -- LOGD("IR function: %s\n", funcName.c_str()); -- this->SetPluginAPIName(""); -- this->SetUserFuncState(STATE_RETURN); -- this->ReceiveSendMsg(funcName, "done"); -+void CreatePhiOpResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ uint64_t argId = atol(root["argId"].asString().c_str()); -+ uint64_t blockId = atol(root["blockId"].asString().c_str()); -+ PhiOp op = clientAPI.CreatePhiOp(argId, blockId); -+ PluginJson json = client->GetJson(); -+ Json::Value opResult = json.PhiOpJsonSerialize(op); -+ client->ReceiveSendMsg("OpsResult", opResult.toStyledString()); - } - --int PluginClient::GetInitInfo(string& serverPath, string& shaPath, int& timeout) -+void CreateConstOpResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- Json::Reader reader; -- std::ifstream ifs; -- string configFilePath; -- string serverDir; -- if (serverPath == "") { -- configFilePath = "/usr/local/bin/pin-gcc-client.json"; // server路径为空,配置文件默认路径 -- LOGW("input serverPath is NULL, read default:%s\n", configFilePath.c_str()); -- } else { -- int index = serverPath.find_last_of("/"); -- serverDir = serverPath.substr(0, index); -- configFilePath = serverDir + "/pin-gcc-client.json"; // 配置文件和server在同一目录 -- } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ PluginJson json = client->GetJson(); -+ PluginIR::PluginTypeBase type = json.TypeJsonDeSerialize(root.toStyledString(), context); -+ uint64_t value = atol(root["value"].asString().c_str()); -+ mlir::OpBuilder opBuilder = mlir::OpBuilder(&context); -+ mlir::Attribute attr = opBuilder.getI64IntegerAttr(value); -+ mlir::Value ret = clientAPI.CreateConstOp(attr, type); -+ client->ReceiveSendMsg("ValueResult", json.ValueJsonSerialize(ret).toStyledString()); -+} - -- ifs.open(configFilePath.c_str()); -- if (!ifs) { -- shaPath = serverDir + "/libpin_user.sha256"; // sha256文件默认和server在同一目录 -- LOGW("open %s fail! use default sha256file:%s\n", configFilePath.c_str(), shaPath.c_str()); -- return -1; -- } -- reader.parse(ifs, root); -- ifs.close(); -+void UpdateSSAResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ bool ret = clientAPI.UpdateSSA(); -+ client->ReceiveSendMsg("BoolResult", std::to_string((uint64_t)ret)); -+} - -- if (serverPath == "") { -- if (!root["path"].isString()) { -- LOGE("path in config.json is not string\n"); -- return 0; -- } -- serverPath = root["path"].asString(); -- int index = serverPath.find_last_of("/"); -- serverDir = serverPath.substr(0, index); -- } -- int timeoutJson = root["timeout"].asInt(); -- if ((timeoutJson >= 50) && (timeoutJson <= 5000)) { // 不在50~5000ms范围内,使用默认值 -- timeout = timeoutJson; -- LOGI("the timeout is:%d\n", timeout); -- } else { -- LOGW("timeout read from %s is:%d,should be 50~5000,use default:%d\n", -- configFilePath.c_str(), timeoutJson, timeout); -- } -- shaPath = root["sha256file"].asString(); -- int ret = access(shaPath.c_str(), F_OK); -- if ((shaPath == "") || (ret != 0)) { -- shaPath = serverDir + "/libpin_user.sha256"; // sha256文件默认和server在同一目录 -- LOGW("sha256 file not found,use default:%s\n", shaPath.c_str()); -- } -- return 0; -+void GetAllPhiOpInsideBlockResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ uint64_t bb = atol(root["bbAddr"].asString().c_str()); -+ vector phiOps = clientAPI.GetPhiOpsInsideBlock(bb); -+ PluginJson json = client->GetJson(); -+ json.GetPhiOpsJsonSerialize(phiOps, result); -+ client->ReceiveSendMsg("GetPhiOps", result); - } - --void PluginClient::GetArg(struct plugin_name_args *pluginInfo, string& serverPath, -- string& arg, LogPriority& logLevel) -+void IsDomInfoAvailableResult(PluginClient *client, Json::Value& root, string& result) - { -- Json::Value root; -- map compileArgs; -- for (int i = 0; i < pluginInfo->argc; i++) { -- string key = pluginInfo->argv[i].key; -- if (key == "server_path") { -- serverPath = pluginInfo->argv[i].value; -- } else if (key == "log_level") { -- logLevel = (LogPriority)atoi(pluginInfo->argv[i].value); -- SetLogPriority(logLevel); -- } else { -- string value = pluginInfo->argv[i].value; -- compileArgs[key] = value; -- root[key] = value; -- } -- } -- arg = root.toStyledString(); -- for (auto it = compileArgs.begin(); it != compileArgs.end(); it++) { -- CheckSafeCompileFlag(it->first, it->second); -- } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ bool ret = clientAPI.IsDomInfoAvailable(); -+ client->ReceiveSendMsg("BoolResult", std::to_string((uint64_t)ret)); - } - --int PluginClient::CheckSHA256(const string& shaPath) -+void GetCurrentDefFromSSAResult(PluginClient *client, Json::Value& root, string& result) - { -- if (shaPath == "") { -- LOGE("sha256file Path is NULL,check:%s\n", shaPath.c_str()); -- return -1; -- } -- int index = shaPath.find_last_of("/"); -- string dir = shaPath.substr(0, index); -- string filename = shaPath.substr(index+1, -1); -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ uint64_t varId = atol(root["varId"].asString().c_str()); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ mlir::Value ret = clientAPI.GetCurrentDefFromSSA(varId); -+ PluginJson json = client->GetJson(); -+ client->ReceiveSendMsg("ValueResult", json.ValueJsonSerialize(ret).toStyledString()); -+} - -- string cmd = "cd " + dir + " && " + "sha256sum -c " + filename + " --quiet"; -- int ret = system(cmd.c_str()); -- return ret; -+void SetCurrentDefInSSAResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ uint64_t varId = atol(root["varId"].asString().c_str()); -+ uint64_t defId = atol(root["defId"].asString().c_str()); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ bool ret = clientAPI.SetCurrentDefInSSA(varId, defId); -+ client->ReceiveSendMsg("BoolResult", std::to_string((uint64_t)ret)); - } - --void PluginClient::CheckSafeCompileFlag(const string& argName, const string& param) -+void CopySSAOpResult(PluginClient *client, Json::Value& root, string& result) - { -- vector safeCompileFlags = { -- "-z noexecstack", -- "-fno-stack-protector", -- "-fstack-protector-all", -- "-D_FORTIFY_SOURCE", -- "-fPic", -- "-fPIE", -- "-fstack-protector-strong", -- "-fvisibility", -- "-ftrapv", -- "-fstack-check", -- }; -- -- for (auto& v : safeCompileFlags) { -- if (param.find(v) != string::npos) { -- LOGW("%s:%s have safe compile parameter:%s !!!\n", argName.c_str(), param.c_str(), v.c_str()); -- } -- } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ uint64_t id = atol(root["id"].asString().c_str()); -+ mlir::Value ret = clientAPI.CopySSAOp(id); -+ PluginJson json = client->GetJson(); -+ client->ReceiveSendMsg("ValueResult", json.ValueJsonSerialize(ret).toStyledString()); - } - --bool PluginClient::DeletePortFromLockFile(unsigned short port) -+void CreateSSAOpResult(PluginClient *client, Json::Value& root, string& result) - { -- int portFileFd = open(g_portFilePath, O_RDWR); -- if (portFileFd == -1) { -- LOGE("%s open file %s fail\n", __func__, g_portFilePath); -- return false; -- } -- LOGI("delete port:%d\n", port); -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ PluginJson json = client->GetJson(); -+ PluginIR::PluginTypeBase type = json.TypeJsonDeSerialize(root.toStyledString(), context); -+ mlir::Value ret = clientAPI.CreateSSAOp(type); -+ client->ReceiveSendMsg("ValueResult", json.ValueJsonSerialize(ret).toStyledString()); -+} - -- flock(portFileFd, LOCK_EX); -- string grpcPorts = ""; -- ReadPortsFromLockFile(portFileFd, grpcPorts); -+void CreateNewDefResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ uint64_t opId = atol(root["opId"].asString().c_str()); -+ uint64_t valueId = atol(root["valueId"].asString().c_str()); -+ uint64_t defId = atol(root["defId"].asString().c_str()); -+ mlir::Value ret = clientAPI.CreateNewDef(valueId, opId, defId); -+ PluginJson json = client->GetJson(); -+ client->ReceiveSendMsg("ValueResult", json.ValueJsonSerialize(ret).toStyledString()); -+} - -- string portStr = std::to_string(port) + "\n"; -- string::size_type pos = grpcPorts.find(portStr); -- if (pos == string::npos) { -- close(portFileFd); -- return true; -- } -- grpcPorts = grpcPorts.erase(pos, portStr.size()); -+void RemoveEdgeResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ /// Json格式 -+ /// { -+ /// "src":"xxxx", -+ /// "dest":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string srcKey = "src"; -+ uint64_t src = atol(root[srcKey].asString().c_str()); -+ std::string destKey = "dest"; -+ uint64_t dest = atol(root[destKey].asString().c_str()); -+ clientAPI.RemoveEdge(src, dest); -+ PluginJson json = client->GetJson(); -+ json.NopJsonSerialize(result); -+ client->ReceiveSendMsg("VoidResult", result); -+} - -- ftruncate(portFileFd, 0); -- lseek(portFileFd, 0, SEEK_SET); -- write(portFileFd, grpcPorts.c_str(), grpcPorts.size()); -- close(portFileFd); -+void ConfirmValueResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ /// Json格式 -+ /// { -+ /// "valId":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string valIdKey = "valId"; -+ uint64_t valId = atol(root[valIdKey].asString().c_str()); -+ mlir::Value v = clientAPI.GetValue(valId); -+ PluginJson json = client->GetJson(); -+ Json::Value valueJson = json.ValueJsonSerialize(v); -+ result = valueJson.toStyledString(); -+ client->ReceiveSendMsg("ValueResult", result); -+} - -- return true; -+void BuildMemRefResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ /// Json格式 -+ /// { -+ /// "baseId":"xxxx", -+ /// "offsetId":"xxxx", -+ /// "type":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string baseIdKey = "baseId"; -+ std::string offsetIdKey = "offsetId"; -+ std::string typeKey = "type"; -+ uint64_t baseId = atol(root[baseIdKey].asString().c_str()); -+ uint64_t offsetId = atol(root[offsetIdKey].asString().c_str()); -+ Json::Value type = root[typeKey]; -+ PluginJson json = client->GetJson(); -+ PluginIR::PluginTypeBase pType = json.TypeJsonDeSerialize(type.toStyledString(), context); -+ mlir::Value v = clientAPI.BuildMemRef(pType, baseId, offsetId); -+ Json::Value valueJson = json.ValueJsonSerialize(v); -+ result = valueJson.toStyledString(); -+ client->ReceiveSendMsg("ValueResult", result); -+} -+ -+void DebugValueResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ /// Json格式 -+ /// { -+ /// "valId":"xxxx", -+ /// } -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ std::string valIdKey = "valId"; -+ uint64_t valId = atol(root[valIdKey].asString().c_str()); -+ clientAPI.DebugValue(valId); -+ PluginJson json = client->GetJson(); -+ json.NopJsonSerialize(result); -+ client->ReceiveSendMsg("ValueResult", result); -+} -+ -+typedef std::function GetResultFunc; -+std::map g_getResultFunc = { -+ {"GetAllFunc", GetAllFuncResult}, -+ {"GetLocalDecls", GetLocalDeclsResult}, -+ {"GetLoopsFromFunc", GetLoopsFromFuncResult}, -+ {"GetLoopById", GetLoopByIdResult}, -+ {"IsBlockInside", IsBlockInsideResult}, -+ {"AllocateNewLoop", AllocateNewLoopResult}, -+ {"RedirectFallthroughTarget", RedirectFallthroughTargetResult}, -+ {"DeleteLoop", DeleteLoopResult}, -+ {"AddBlockToLoop", AddBlockToLoopResult}, -+ {"AddLoop", AddLoopResult}, -+ {"GetBlocksInLoop", GetBlocksInLoopResult}, -+ {"GetHeader", GetHeaderResult}, -+ {"GetLatch", GetLatchResult}, -+ {"SetHeader", SetHeaderResult}, -+ {"SetLatch", SetLatchResult}, -+ {"GetLoopExits", GetLoopExitsResult}, -+ {"GetLoopSingleExit", GetLoopSingleExitResult}, -+ {"GetBlockLoopFather", GetBlockLoopFatherResult}, -+ {"CreateBlock", CreateBlockResult}, -+ {"DeleteBlock", DeleteBlockResult}, -+ {"SetImmediateDominator", SetImmediateDominatorResult}, -+ {"GetImmediateDominator", GetImmediateDominatorResult}, -+ {"RecomputeDominator", RecomputeDominatorResult}, -+ {"GetPhiOp", GetPhiOpResult}, -+ {"GetCallOp", GetCallOpResult}, -+ {"SetLhsInCallOp", SetLhsInCallOpResult}, -+ {"AddArgInPhiOp", AddArgInPhiOpResult}, -+ {"CreateCallOp", CreateCallOpResult}, -+ {"CreateAssignOp", CreateAssignOpResult}, -+ {"CreateCondOp", CreateCondOpResult}, -+ {"CreateFallthroughOp", CreateFallthroughOpResult}, -+ {"GetResultFromPhi", GetResultFromPhiResult}, -+ {"CreatePhiOp", CreatePhiOpResult}, -+ {"CreateConstOp", CreateConstOpResult}, -+ {"UpdateSSA", UpdateSSAResult}, -+ {"GetAllPhiOpInsideBlock", GetAllPhiOpInsideBlockResult}, -+ {"IsDomInfoAvailable", IsDomInfoAvailableResult}, -+ {"GetCurrentDefFromSSA", GetCurrentDefFromSSAResult}, -+ {"SetCurrentDefInSSA", SetCurrentDefInSSAResult}, -+ {"CopySSAOp", CopySSAOpResult}, -+ {"CreateSSAOp", CreateSSAOpResult}, -+ {"CreateNewDef", CreateNewDefResult}, -+ {"RemoveEdge", RemoveEdgeResult}, -+ {"ConfirmValue", ConfirmValueResult}, -+ {"BuildMemRef", BuildMemRefResult}, -+ {"DebugValue", DebugValueResult}, -+}; -+ -+void PluginClient::GetIRTransResult(void *gccData, const string& funcName, const string& param) -+{ -+ string result; -+ Json::Value root; -+ Json::Reader reader; -+ reader.parse(param, root); -+ LOGD("%s func:%s,param:%s\n", __func__, funcName.c_str(), param.c_str()); -+ -+ if (funcName == "GetInjectDataAddress") { -+ int64_t ptrAddress = (int64_t)gccData; -+ json.IntegerSerialize(ptrAddress, result); -+ this->ReceiveSendMsg("IntegerResult", result); -+ } else if (funcName == "GetIncludeFile") { -+ if (gccData != nullptr) { -+ string includeFile = (char *)gccData; -+ json.StringSerialize(includeFile, result); -+ this->ReceiveSendMsg("StringResult", result); -+ } else { -+ LOGE("%s gcc_data address is NULL!\n", __func__); -+ } -+ } else { -+ auto it = g_getResultFunc.find(funcName); -+ if (it != g_getResultFunc.end()) { -+ it->second(this, root, result); -+ } else { -+ string key = ""; -+ GetGccData(funcName, param, key, result); -+ if (key != "") { -+ this->ReceiveSendMsg(key, result); -+ } -+ } -+ } -+ -+ LOGD("IR function: %s\n", funcName.c_str()); -+ this->SetPluginAPIName(""); -+ this->SetUserFuncState(STATE_RETURN); -+ this->ReceiveSendMsg(funcName, "done"); - } - -+// attribute:value说明 -+// start:ok 启动成功 -+// stop:ok 关闭成功 -+// userFunc:execution completed 函数执行完毕,执行下一个函数 -+// injectPoit:xxxx 注册点,xxxx是server传递过来的唯一值,gcc回调时,将xxxx返回server解析,执行对应的函数 -+// xxxx:yyyy 此类都认为是api函数,xxxx为函数名,yyyy为形参 - void PluginClient::ReceiveSendMsg(const string& attribute, const string& value) - { - ClientContext context; -@@ -1219,31 +925,29 @@ void PluginClient::ReceiveSendMsg(const string& attribute, const string& value) - clientMsg.set_value(value); - stream->Write(clientMsg); - stream->WritesDone(); -- TimerStart(timeout); -- if (g_grpcChannel->GetState(true) != GRPC_CHANNEL_READY) { -+ TimerStart(input.GetTimeout()); -+ -+ if (grpcChannel->GetState(true) != GRPC_CHANNEL_READY) { - LOGW("client pid:%d grpc channel not ready!\n", getpid()); - return; - } - ServerMsg serverMsg; - while (stream->Read(&serverMsg)) { -- TimerStart(0); -- if (serverMsg.attribute() != "injectPoint") { // 日志不记录注册的函数名信息 -+ TimerStart(0); // 定时值0,关闭定时器 -+ if (serverMsg.attribute() != grpckey[INJECT]) { // 日志不记录注册的函数名信息 - LOGD("rec from server:%s,%s\n", serverMsg.attribute().c_str(), serverMsg.value().c_str()); - } -- if ((serverMsg.attribute() == "start") && (serverMsg.value() == "ok")) { -+ if ((serverMsg.attribute() == grpckey[START]) && (serverMsg.value() == grpcValue[START_VALUE])) { - LOGI("server has been started!\n"); -- if (!DeletePortFromLockFile(GetGrpcPort())) { -- LOGE("DeletePortFromLockFile fail\n"); -- } -- } else if ((serverMsg.attribute() == "stop") && (serverMsg.value() == "ok")) { -+ DeleteGrpcPort(); -+ } else if ((serverMsg.attribute() == grpckey[STOP]) && (serverMsg.value() == grpcValue[STOP_VALUE])) { - LOGI("server has been closed!\n"); - Status status = stream->Finish(); - if (!status.ok()) { -- LOGE("error code:%d,%s\n", status.error_code(), status.error_message().c_str()); -- LOGE("RPC failed\n"); -+ LOGE("RPC failed,error code:%d,%s\n", status.error_code(), status.error_message().c_str()); - } - CloseLog(); -- } else if ((serverMsg.attribute() == "userFunc") && (serverMsg.value() == "execution completed")) { -+ } else if ((serverMsg.attribute() == grpckey[USERFUNC]) && (serverMsg.value() == grpcValue[USERFUNC_VALUE])) { - SetUserFuncState(STATE_END); // server已接收到对应函数所需数据 - } else { - ServerMsgProc(serverMsg.attribute(), serverMsg.value()); -@@ -1258,9 +962,10 @@ int PluginClient::AddRegisteredUserFunc(const string& value) - string name = value.substr(index + 1, -1); - InjectPoint inject = (InjectPoint)atoi(point.c_str()); - if (inject >= HANDLE_MAX) { -+ LOGE("AddRegisteredUserFunc %s err!\n", value.c_str()); - return -1; - } -- -+ - registeredUserFunc[inject].push_back(name); - return 0; - } -@@ -1270,63 +975,75 @@ ManagerSetupData GetPassInfoData(const string& data) - ManagerSetupData setupData; - Json::Value root; - Json::Reader reader; -- reader.parse(data, root); -+ string jsonData = data.substr(data.find_first_of(":") + 1, -1); -+ reader.parse(jsonData, root); - -- setupData.refPassName = (RefPassName)root["refPassName"].asInt(); -- setupData.passNum = root["passNum"].asInt(); -- setupData.passPosition = (PassPosition)root["passPosition"].asInt(); -+ if (root[passValue[PASS_NAME]].isInt()) { -+ setupData.refPassName = (RefPassName)root[passValue[PASS_NAME]].asInt(); -+ } -+ if (root[passValue[PASS_NUM]].isInt()) { -+ setupData.passNum = root[passValue[PASS_NUM]].asInt(); -+ } -+ if (root[passValue[PASS_POSITION]].isInt()) { -+ setupData.passPosition = (PassPosition)root[passValue[PASS_POSITION]].asInt(); -+ } -+ - return setupData; - } - -+// attribute不为"injectPoint"时,attribute为"funcName",value为函数参数 -+// attribute为"injectPoint"时, value为"inject:funcName+函数指针地址" - void PluginClient::ServerMsgProc(const string& attribute, const string& value) - { -- std::shared_ptr client = PluginClient::GetInstance(); -- if (attribute == "injectPoint") { -- if (value == "finished") { -- string pluginName = client->GetPluginName(); -- InjectPoint inject; -- map> userFuncs = client->GetRegisteredUserFunc(); -- for (auto it = userFuncs.begin(); it != userFuncs.end(); it++) { -- inject = it->first; -- if (inject == HANDLE_MANAGER_SETUP) { -- string data = it->second.front(); -- ManagerSetupData setupData = GetPassInfoData(data); -- RegisterPassManagerSetup(inject, setupData, pluginName); -- } else { -- RegisterPluginEvent(inject, pluginName); // 注册event -+ if (attribute != grpckey[INJECT]) { -+ SetPluginAPIParam(value); -+ SetPluginAPIName(attribute); -+ SetUserFuncState(STATE_BEGIN); -+ return; -+ } -+ -+ if (value == grpcValue[FINISH_VALUE]) { -+ string pluginName = GetPluginName(); -+ InjectPoint inject; -+ map> userFuncs = GetRegisteredUserFunc(); -+ for (auto it = userFuncs.begin(); it != userFuncs.end(); it++) { -+ inject = it->first; // first为注册点 -+ if (inject == HANDLE_MANAGER_SETUP) { -+ for (unsigned int i = 0; i < it->second.size(); i++) { -+ ManagerSetupData setupData = GetPassInfoData(it->second[i]); -+ RegisterPassManagerSetup(i, setupData, pluginName); - } -+ } else { -+ RegisterPluginEvent(inject, pluginName); // 注册event - } -- client->SetInjectFlag(true); -- } else { -- client->AddRegisteredUserFunc(value); - } -+ SetInjectFlag(true); - } else { -- client->SetPluginAPIParam(value); -- client->SetPluginAPIName(attribute); -- client->SetUserFuncState(STATE_BEGIN); -+ AddRegisteredUserFunc(value); - } - } - - void TimeoutFunc(union sigval sig) - { - LOGW("client pid:%d timeout!\n", getpid()); -- g_plugin->SetUserFuncState(STATE_TIMEOUT); -+ PluginClient::GetInstance()->SetUserFuncState(STATE_TIMEOUT); - } - - void PluginClient::TimerStart(int interval) - { -- int msTons = 1000000; // ms转ns倍数 -- int msTos = 1000; // s转ms倍数 -+ const int msTons = 1000000; // ms转ns倍数 -+ const int msTos = 1000; // s转ms倍数 - struct itimerspec time_value; - time_value.it_value.tv_sec = (interval / msTos); - time_value.it_value.tv_nsec = (interval % msTos) * msTons; - time_value.it_interval.tv_sec = 0; - time_value.it_interval.tv_nsec = 0; -- -- timer_settime(timerId, 0, &time_value, NULL); -+ -+ const int timeFlag = 0; // 0 表示希望timer首次到期时的时间与启动timer的时间间隔 -+ timer_settime(timerId, timeFlag, &time_value, NULL); - } - --bool PluginClient::TimerInit(void) -+bool PluginClient::TimerInit(clockid_t id) - { - struct sigevent evp; - int sival = 124; // 传递整型参数,可以自定义 -@@ -1336,130 +1053,122 @@ bool PluginClient::TimerInit(void) - evp.sigev_notify = SIGEV_THREAD; - evp.sigev_notify_function = TimeoutFunc; - -- if (timer_create(CLOCK_REALTIME, &evp, &timerId) == -1) { -+ if (timer_create(id, &evp, &timerId) == -1) { - LOGE("timer create fail\n"); - return false; - } - return true; - } - --int PluginClient::OpenLockFile(const char *path) -+static bool WaitServer(const string& port) - { -- int portFileFd = -1; -- if (access(path, F_OK) == -1) { -- mode_t mask = umask(0); -- mode_t mode = 0666; -- portFileFd = open(path, O_CREAT | O_RDWR, mode); -- umask(mask); -- } else { -- portFileFd = open(path, O_RDWR); -- } -- -- if (portFileFd == -1) { -- LOGE("open file %s fail\n", path); -- } -- return portFileFd; --} -- --void PluginClient::ReadPortsFromLockFile(int fd, string& grpcPorts) --{ -- int fileLen = lseek(fd, 0, SEEK_END); -- lseek(fd, 0, SEEK_SET); -- char *buf = new char[fileLen + 1]; -- read(fd, buf, fileLen); -- buf[fileLen] = '\0'; -- grpcPorts = buf; -- delete[] buf; --} -- --unsigned short PluginClient::FindUnusedPort(void) --{ -- unsigned short basePort = 40000; // grpc通信端口号从40000开始 -- unsigned short foundPort = 0; // 可以使用的端口号 -- struct sockaddr_in serverAddr; -- memset(&serverAddr, 0, sizeof(serverAddr)); -- serverAddr.sin_family = AF_INET; -- serverAddr.sin_addr.s_addr = inet_addr("0.0.0.0"); -- -- int portFileFd = OpenLockFile(g_portFilePath); -- if (portFileFd == -1) { -- return 0; -- } -- -- flock(portFileFd, LOCK_EX); -- string grpcPorts = ""; -- ReadPortsFromLockFile(portFileFd, grpcPorts); -- -- while (++basePort < UINT16_MAX) { -- int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); -- serverAddr.sin_port = htons(basePort); -- int ret = connect(sock, (struct sockaddr*)&serverAddr, sizeof(serverAddr)); -- if (sock != -1) { -- close(sock); -- } -- if ((ret == -1) && (errno == ECONNREFUSED)) { -- string strPort = std::to_string(basePort) + "\n"; -- if (grpcPorts.find(strPort) == grpcPorts.npos) { -- foundPort = basePort; -- LOGI("found port:%d\n", foundPort); -- lseek(portFileFd, 0, SEEK_END); -- write(portFileFd, strPort.c_str(), strPort.size()); -- break; -- } -+ const int delay = 50; -+ const int cnt = 4000; -+ mode_t mask = umask(0); -+ mode_t mode = 0666; // 权限是rwrwrw,跨进程时,其他用户也要可以访问 -+ string semFile = "wait_server_startup" + port; -+ sem_t *sem = sem_open(semFile.c_str(), O_CREAT, mode, 0); -+ umask(mask); -+ int i = 0; -+ for (; i < cnt; i++) { -+ if (sem_trywait(sem) == 0) { -+ break; -+ } else { -+ usleep(delay); - } - } -- -- if (basePort == UINT16_MAX) { -- ftruncate(portFileFd, 0); -- lseek(portFileFd, 0, SEEK_SET); -+ sem_close(sem); -+ sem_unlink(semFile.c_str()); -+ if (i >= cnt) { -+ return false; - } -- -- close(portFileFd); // 关闭文件fd会同时释放文件锁 -- return foundPort; -+ return true; - } - --int ServerStart(int timeout, const string& serverPath, pid_t& pid, string& port, const LogPriority logLevel) -+int PluginClient::ServerStart(pid_t& pid) - { -- unsigned short portNum = PluginClient::FindUnusedPort(); -- if (portNum == 0) { -+ if (!grpcPort.FindUnusedPort()) { - LOGE("cannot find port for grpc,port 40001-65535 all used!\n"); - return -1; - } -+ - int ret = 0; -- port = std::to_string(portNum); -+ unsigned short portNum = grpcPort.GetPort(); -+ string port = std::to_string(portNum); - pid = vfork(); - if (pid == 0) { - LOGI("start plugin server!\n"); -- string paramTimeout = std::to_string(timeout); -- if (execl(serverPath.c_str(), paramTimeout.c_str(), port.c_str(), -- std::to_string(logLevel).c_str(), NULL) == -1) { -- PluginClient::DeletePortFromLockFile(portNum); -+ string serverPath = input.GetServerPath(); -+ if (execl(serverPath.c_str(), port.c_str(), std::to_string(input.GetLogLevel()).c_str(), NULL) == -1) { -+ DeleteGrpcPort(); - LOGE("server start fail! please check serverPath:%s\n", serverPath.c_str()); - ret = -1; - _exit(0); - } - } -- int delay = 500000; // 500ms -- usleep(delay); // wait server start - -+ if (!WaitServer(port)) { -+ ret = -1; -+ } - return ret; - } - --int ClientStart(int timeout, const string& arg, const string& pluginName, const string& port) -+int PluginClient::ClientStart() - { -- string serverPort = "localhost:" + port; -- g_grpcChannel = grpc::CreateChannel(serverPort, grpc::InsecureChannelCredentials()); -- g_plugin = std::make_shared(g_grpcChannel); -- g_plugin->SetInjectFlag(false); -- g_plugin->SetTimeout(timeout); -- g_plugin->SetUserFuncState(STATE_WAIT_BEGIN); -- -- if (!g_plugin->TimerInit()) { -- return 0; -+ setenv("no_grpc_proxy", "localhost", 1); // 关闭localhost的代理,因为server和client运行在同一个机器上,需要通过localhost建立连接 -+ string serverPort = "localhost:" + std::to_string(grpcPort.GetPort()); -+ grpcChannel = grpc::CreateChannel(serverPort, grpc::InsecureChannelCredentials()); -+ serviceStub = PluginService::NewStub(grpcChannel); -+ SetInjectFlag(false); -+ SetUserFuncState(STATE_WAIT_BEGIN); -+ SetStartFlag(true); -+ if (!TimerInit(CLOCK_REALTIME)) { -+ return -1; - } -- unsigned short grpcPort = (unsigned short)atoi(port.c_str()); -- g_plugin->SetGrpcPort(grpcPort); -- g_plugin->ReceiveSendMsg("start", arg); -+ ReceiveSendMsg(grpckey[START], input.GetArgs()); - return 0; - } -+ -+void PluginClient::Init(struct plugin_name_args *pluginInfo, const string& pluginName, pid_t& serverPid) -+{ -+ SetPluginName(pluginName); -+ SetStartFlag(false); -+ -+ // inputCheck模块初始化,并对输入参数进行检查 -+ input.GetInputArgs(pluginInfo); -+ if (input.GetInitInfo() != 0) { -+ LOGD("read default info from pin-gcc-client.json fail! use the default timeout=%dms\n", input.GetTimeout()); -+ } -+ if (input.GetServerPath() == "") { -+ LOGE("server path is NULL!\n"); -+ return; -+ } -+ if (input.CheckSHA256() != 0) { -+ LOGE("sha256 check sha256 file:%s fail!\n", input.GetShaPath().c_str()); -+ return; -+ } else { -+ LOGI("sha256 check success!\n"); -+ } -+ -+ // 查找未使用的端口号,并启动server和client -+ if (ServerStart(serverPid) != 0) { -+ DeleteGrpcPort(); -+ return; -+ } -+ if (ClientStart() != 0) { -+ return; -+ } -+ -+ // 等待用户完成注入点的注册或者server异常退出后,再接着执行gcc编译 -+ int status; -+ while (1) { -+ if ((GetInjectFlag()) || (GetUserFuncState() == STATE_TIMEOUT)) { -+ break; -+ } -+ if (serverPid == waitpid(-1, &status, WNOHANG)) { -+ DeleteGrpcPort(); -+ break; -+ } -+ } -+} - } // namespace PinClient --- -2.27.0.windows.1 - diff --git a/0003-Refactoring-Code-refactoring-of-Communication-Subsys.patch b/0003-Refactoring-Code-refactoring-of-Communication-Subsys.patch deleted file mode 100644 index 010ca02..0000000 --- a/0003-Refactoring-Code-refactoring-of-Communication-Subsys.patch +++ /dev/null @@ -1,839 +0,0 @@ -From 1e14f9e453c9504cff141ce9059c0eeb405e73ed Mon Sep 17 00:00:00 2001 -From: huitailangzju <804544223@qq.com> -Date: Tue, 7 Feb 2023 19:18:42 +0800 -Subject: [PATCH 3/9] [Refactoring] Code refactoring of Communication Subsystem - [2/3]. Add PluginGrpcPort, PluginInputCheck and PluginJson. - - -diff --git a/lib/PluginClient/PluginGrpcPort.cpp b/lib/PluginClient/PluginGrpcPort.cpp -new file mode 100755 -index 0000000..c6b9a1f ---- /dev/null -+++ b/lib/PluginClient/PluginGrpcPort.cpp -@@ -0,0 +1,159 @@ -+/* Copyright (c) 2022-2022 Huawei Technologies Co., Ltd. -+ -+ This program is free software; you can redistribute it and/or modify it -+ under the terms of the GNU General Public License as published by the -+ Free Software Foundation; either version 3, or (at your option) any -+ later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program; see the file COPYING3. If not see -+ . -+ -+ Author: Mingchuan Wu and Yancheng Li -+ Create: 2022-08-18 -+ Description: -+ This file contains the implementation of the PluginGrpcPort class. -+ 主要完成功能:查找未使用的端口号,并将端口号写入到共享文件中,通过文件锁控制多进程间访问,并提供 -+ DeletePortFromLockFile接口在退出时删除写入的端口号 -+*/ -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "PluginClient/PluginLog.h" -+#include "PluginClient/PluginGrpcPort.h" -+ -+namespace PinClient { -+int PluginGrpcPort::OpenFile(const char *path) -+{ -+ int portFileFd = -1; -+ if (access(path, F_OK) == -1) { -+ mode_t mask = umask(0); -+ mode_t mode = 0666; // 权限是rwrwrw,跨进程时,其他用户也要可以访问 -+ portFileFd = open(path, O_CREAT | O_RDWR, mode); -+ umask(mask); -+ } else { -+ portFileFd = open(path, O_RDWR); -+ } -+ -+ if (portFileFd == -1) { -+ LOGE("open file %s fail\n", path); -+ } -+ return portFileFd; -+} -+ -+bool PluginGrpcPort::ReadPortsFromLockFile(int fd, string& grpcPorts) -+{ -+ if (flock(fd, LOCK_EX) != 0) { -+ return false; -+ } -+ -+ int fileLen = lseek(fd, 0, SEEK_END); -+ lseek(fd, 0, SEEK_SET); -+ char *buf = new char[fileLen + 1]; -+ if (buf == NULL) { -+ return false; -+ } -+ if (read(fd, buf, fileLen) < 0) { -+ return false; -+ } -+ buf[fileLen] = '\0'; -+ grpcPorts = buf; -+ delete[] buf; -+ return true; -+} -+ -+bool PluginGrpcPort::FindUnusedPort() -+{ -+ unsigned short basePort = GetBasePort(); -+ int portFileFd = OpenFile(lockFilePath.c_str()); -+ if (portFileFd == -1) { -+ return false; -+ } -+ -+ string grpcPorts = ""; -+ if (!ReadPortsFromLockFile(portFileFd, grpcPorts)) { -+ close(portFileFd); -+ return false; -+ } -+ -+ // 不使用UINT16_MAX端口号,端口号为UINT16_MAX时作异常处理 -+ while (++basePort < UINT16_MAX) { -+ int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); -+ struct sockaddr_in serverAddr; -+ memset(&serverAddr, 0, sizeof(serverAddr)); -+ serverAddr.sin_family = AF_INET; -+ serverAddr.sin_addr.s_addr = inet_addr("0.0.0.0"); -+ serverAddr.sin_port = htons(basePort); -+ int ret = connect(sock, (struct sockaddr*)&serverAddr, sizeof(serverAddr)); -+ if (sock != -1) { -+ close(sock); -+ } -+ if ((ret == -1) && (errno == ECONNREFUSED)) { -+ string strPort = std::to_string(basePort) + "\n"; -+ if (grpcPorts.find(strPort) == grpcPorts.npos) { -+ port = basePort; -+ LOGI("found port:%d\n", port); -+ lseek(portFileFd, 0, SEEK_END); -+ write(portFileFd, strPort.c_str(), strPort.size()); -+ break; -+ } -+ } -+ } -+ -+ if (basePort == UINT16_MAX) { -+ ftruncate(portFileFd, 0); // 清空锁文件,避免异常未删除释放的端口号,导致无端口使用 -+ lseek(portFileFd, 0, SEEK_SET); -+ close(portFileFd); // 关闭文件fd会同时释放文件锁 -+ return false; -+ } -+ -+ close(portFileFd); // 关闭文件fd会同时释放文件锁 -+ return true; -+} -+ -+bool PluginGrpcPort::DeletePortFromLockFile() -+{ -+ if (port == 0) { -+ return true; -+ } -+ int portFileFd = open(lockFilePath.c_str(), O_RDWR); -+ if (portFileFd == -1) { -+ LOGE("%s open file %s fail\n", __func__, lockFilePath.c_str()); -+ return false; -+ } -+ LOGI("delete port:%d\n", port); -+ -+ string grpcPorts = ""; -+ if (!ReadPortsFromLockFile(portFileFd, grpcPorts)) { -+ close(portFileFd); -+ port = 0; -+ return false; -+ } -+ -+ string portStr = std::to_string(port) + "\n"; -+ string::size_type pos = grpcPorts.find(portStr); -+ if (pos == string::npos) { -+ close(portFileFd); -+ port = 0; -+ return true; -+ } -+ grpcPorts = grpcPorts.erase(pos, portStr.size()); -+ -+ ftruncate(portFileFd, 0); -+ lseek(portFileFd, 0, SEEK_SET); -+ write(portFileFd, grpcPorts.c_str(), grpcPorts.size()); -+ close(portFileFd); -+ port = 0; -+ return true; -+} -+} // namespace PinClient -diff --git a/lib/PluginClient/PluginInputCheck.cpp b/lib/PluginClient/PluginInputCheck.cpp -new file mode 100755 -index 0000000..12cfcdc ---- /dev/null -+++ b/lib/PluginClient/PluginInputCheck.cpp -@@ -0,0 +1,194 @@ -+/* Copyright (c) 2022-2022 Huawei Technologies Co., Ltd. -+ -+ This program is free software; you can redistribute it and/or modify it -+ under the terms of the GNU General Public License as published by the -+ Free Software Foundation; either version 3, or (at your option) any -+ later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program; see the file COPYING3. If not see -+ . -+ -+ Author: Mingchuan Wu and Yancheng Li -+ Create: 2022-08-18 -+ Description: -+ This file contains the implementation of the PluginInputCheck class. -+*/ -+ -+#include -+#include "PluginClient/PluginInputCheck.h" -+ -+namespace PinClient { -+// 对server可执行文件进行检查,后续可以在此函数中扩展是否可执行,文件权限等检查 -+int PluginInputCheck::CheckServerFile() -+{ -+ int ret = access(serverPath.c_str(), F_OK); -+ return ret; -+} -+ -+// 对sha256文件进行检查,后续可以在此函数中扩展文件权限、是否为空等检查 -+int PluginInputCheck::CheckShaFile() -+{ -+ int ret = access(shaPath.c_str(), F_OK); -+ return ret; -+} -+ -+bool PluginInputCheck::ReadConfigfile(Json::Value& root) -+{ -+ Json::Reader reader; -+ std::ifstream ifs(configFilePath.c_str()); -+ if (!ifs.is_open()) { -+ LOGW("open %s fail! use default sha256file:%s\n", configFilePath.c_str(), shaPath.c_str()); -+ return false; -+ } -+ -+ if (!reader.parse(ifs, root)) { -+ printf("parse %s fail! check the file format!\n", configFilePath.c_str()); -+ ifs.close(); -+ return false; -+ } -+ -+ ifs.close(); -+ return true; -+} -+ -+void PluginInputCheck::SetTimeout(int time) -+{ -+ const int timeoutMin = 50; -+ const int timeoutMax = 5000; -+ if ((time >= timeoutMin) && (time <= timeoutMax)) { // 不在50~5000ms范围内,使用默认值 -+ timeout = time; -+ LOGI("the timeout is:%d\n", timeout); -+ return; -+ } -+ LOGW("SetTimeout:%d,should be 50~5000,use default:%d\n", time, timeout); -+} -+ -+int PluginInputCheck::GetInitInfo() -+{ -+ Json::Value root; -+ if (!ReadConfigfile(root)) { -+ return -1; -+ } -+ -+ if (serverPath == "") { -+ if (root[jsonkey[PATH]].isString()) { -+ serverPath = root[jsonkey[PATH]].asString(); -+ } else { -+ LOGW("serverPath in config.json is not string!\n"); -+ } -+ } -+ if (CheckServerFile() != 0) { -+ LOGE("serverPath:%s not exist!\n", serverPath.c_str()); -+ serverPath = ""; -+ return -1; -+ } -+ -+ if (root[jsonkey[TIMEOUT]].isInt()) { -+ int timeoutJson = root[jsonkey[TIMEOUT]].asInt(); -+ SetTimeout(timeoutJson); -+ } -+ -+ if (root[jsonkey[SHA256]].isString()) { -+ shaPath = root[jsonkey[SHA256]].asString(); -+ } else { -+ LOGW("sha256file int config.json is not string!\n"); -+ } -+ -+ if ((shaPath == "") || (CheckShaFile() != 0)) { -+ shaPath = GetServerDir() + shaFile; // sha256文件默认和server在同一目录 -+ LOGW("sha256 file not found,use default:%s\n", shaPath.c_str()); -+ } -+ return 0; -+} -+ -+std::map g_keyMap { -+ {"server_path", KEY_SERVER_PATH}, -+ {"log_level", KEY_LOG_LEVEL}, -+}; -+void PluginInputCheck::GetInputArgs(struct plugin_name_args *pluginInfo) -+{ -+ Json::Value root; -+ map compileArgs; -+ -+ for (int i = 0; i < pluginInfo->argc; i++) { -+ string key = pluginInfo->argv[i].key; -+ int keyTag = -1; -+ auto it = g_keyMap.find(key); -+ if (it != g_keyMap.end()) { -+ keyTag = it->second; -+ } -+ switch (keyTag) { -+ case KEY_SERVER_PATH: -+ serverPath = pluginInfo->argv[i].value; -+ if (serverPath != "") { -+ configFilePath = GetServerDir() + "/pin-gcc-client.json"; // 配置文件和server在同一目录 -+ shaPath = GetServerDir() + shaFile; // sha256文件默认和server在同一目录 -+ } -+ break; -+ case KEY_LOG_LEVEL: -+ logLevel = (LogPriority)atoi(pluginInfo->argv[i].value); -+ SetLogPriority(logLevel); -+ break; -+ default: -+ string value = pluginInfo->argv[i].value; -+ compileArgs[key] = value; -+ root[key] = value; -+ break; -+ } -+ } -+ -+ args = root.toStyledString(); -+ for (auto it = compileArgs.begin(); it != compileArgs.end(); it++) { -+ CheckSafeCompileFlag(it->first, it->second); -+ } -+} -+ -+int PluginInputCheck::CheckSHA256() -+{ -+ if (shaPath == "") { -+ LOGE("sha256file Path is NULL!\n"); -+ return -1; -+ } -+ int index = shaPath.find_last_of("/"); -+ string dir = shaPath.substr(0, index); -+ string filename = shaPath.substr(index+1, -1); -+ -+ string cmd = "cd " + dir + " && " + "sha256sum -c " + filename + " --quiet"; -+ int ret = system(cmd.c_str()); -+ return ret; -+} -+ -+void PluginInputCheck::CheckSafeCompileFlag(const string& argName, const string& param) -+{ -+ for (auto& compileFlag : safeCompileFlags) { -+ if (param.find(compileFlag) != string::npos) { -+ LOGW("%s:%s have safe compile parameter:%s !!!\n", argName.c_str(), param.c_str(), compileFlag.c_str()); -+ } -+ } -+} -+ -+PluginInputCheck::PluginInputCheck() -+{ -+ shaPath = ""; -+ serverPath = ""; -+ logLevel = PRIORITY_WARN; -+ timeout = 200; // 默认超时时间200ms -+ SetConfigPath("/usr/local/bin/pin-gcc-client.json"); -+ safeCompileFlags.push_back("-z noexecstack"); -+ safeCompileFlags.push_back("-fno-stack-protector"); -+ safeCompileFlags.push_back("-fstack-protector-all"); -+ safeCompileFlags.push_back("-D_FORTIFY_SOURCE"); -+ safeCompileFlags.push_back("-fPic"); -+ safeCompileFlags.push_back("-fPIE"); -+ safeCompileFlags.push_back("-fstack-protector-strong"); -+ safeCompileFlags.push_back("-fvisibility"); -+ safeCompileFlags.push_back("-ftrapv"); -+ safeCompileFlags.push_back("-fstack-check"); -+} -+} // namespace PinClient -diff --git a/lib/PluginClient/PluginJson.cpp b/lib/PluginClient/PluginJson.cpp -new file mode 100755 -index 0000000..22cd489 ---- /dev/null -+++ b/lib/PluginClient/PluginJson.cpp -@@ -0,0 +1,458 @@ -+/* Copyright (c) 2022-2022 Huawei Technologies Co., Ltd. -+ -+ This program is free software; you can redistribute it and/or modify it -+ under the terms of the GNU General Public License as published by the -+ Free Software Foundation; either version 3, or (at your option) any -+ later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program; see the file COPYING3. If not see -+ . -+ -+ Author: Mingchuan Wu and Yancheng Li -+ Create: 2022-08-18 -+ Description: -+ This file contains the implementation of the PluginJson class. -+*/ -+ -+#include -+#include "PluginAPI/PluginClientAPI.h" -+#include "PluginClient/PluginLog.h" -+#include "PluginClient/PluginJson.h" -+ -+namespace PinClient { -+using std::map; -+using namespace mlir::Plugin; -+using namespace mlir; -+ -+static uintptr_t GetID(Json::Value node) -+{ -+ string id = node.asString(); -+ return atol(id.c_str()); -+} -+ -+Json::Value PluginJson::TypeJsonSerialize(PluginIR::PluginTypeBase& type) -+{ -+ Json::Value root; -+ Json::Value operationObj; -+ Json::Value item; -+ -+ uint64_t ReTypeId; -+ uint64_t ReTypeWidth; -+ -+ ReTypeId = static_cast(type.getPluginTypeID()); -+ item["id"] = std::to_string(ReTypeId); -+ -+ if (auto elemTy = type.dyn_cast()) { -+ auto baseTy = elemTy.getElementType().dyn_cast(); -+ item["elementType"] = TypeJsonSerialize(baseTy); -+ if (elemTy.isReadOnlyElem()) { -+ item["elemConst"] = "1"; -+ } else { -+ item["elemConst"] = "0"; -+ } -+ } -+ -+ if (type.getPluginIntOrFloatBitWidth() != 0) { -+ ReTypeWidth = type.getPluginIntOrFloatBitWidth(); -+ item["width"] = std::to_string(ReTypeWidth); -+ } -+ -+ if (type.isSignedPluginInteger()) { -+ item["signed"] = "1"; -+ } -+ -+ if (type.isUnsignedPluginInteger()) { -+ item["signed"] = "0"; -+ } -+ -+ root["type"] = item; -+ return root; -+} -+ -+PluginIR::PluginTypeBase PluginJson::TypeJsonDeSerialize(const string& data, mlir::MLIRContext &context) -+{ -+ Json::Value root; -+ Json::Reader reader; -+ Json::Value node; -+ reader.parse(data, root); -+ -+ PluginIR::PluginTypeBase baseType; -+ -+ Json::Value type = root["type"]; -+ uint64_t id = GetID(type["id"]); -+ PluginIR::PluginTypeID PluginTypeId = static_cast(id); -+ -+ if (type["signed"] && (id >= static_cast(PluginIR::UIntegerTy1ID) -+ && id <= static_cast(PluginIR::IntegerTy64ID))) { -+ string s = type["signed"].asString(); -+ uint64_t width = GetID(type["width"]); -+ if (s == "1") { -+ baseType = PluginIR::PluginIntegerType::get(&context, width, PluginIR::PluginIntegerType::Signed); -+ } else { -+ baseType = PluginIR::PluginIntegerType::get(&context, width, PluginIR::PluginIntegerType::Unsigned); -+ } -+ } else if (type["width"] && (id == static_cast(PluginIR::FloatTyID) -+ || id == static_cast(PluginIR::DoubleTyID))) { -+ uint64_t width = GetID(type["width"]); -+ baseType = PluginIR::PluginFloatType::get(&context, width); -+ } else if (id == static_cast(PluginIR::PointerTyID)) { -+ mlir::Type elemTy = TypeJsonDeSerialize(type["elementType"].toStyledString(), context); -+ baseType = PluginIR::PluginPointerType::get(&context, elemTy, type["elemConst"].asString() == "1" ? 1 : 0); -+ } else { -+ if (PluginTypeId == PluginIR::VoidTyID) { -+ baseType = PluginIR::PluginVoidType::get(&context); -+ } -+ if (PluginTypeId == PluginIR::BooleanTyID) { -+ baseType = PluginIR::PluginBooleanType::get(&context); -+ } -+ if (PluginTypeId == PluginIR::UndefTyID) { -+ baseType = PluginIR::PluginUndefType::get(&context); -+ } -+ } -+ -+ return baseType; -+} -+ -+void PluginJson::FunctionOpJsonSerialize(vector& data, string& out) -+{ -+ Json::Value root; -+ Json::Value operationObj; -+ Json::Value item; -+ -+ int i = 0; -+ string operation; -+ -+ for (auto& d: data) { -+ item["id"] = std::to_string(d.idAttr().getInt()); -+ if (d.declaredInlineAttr().getValue()) { -+ item["attributes"]["declaredInline"] = "1"; -+ } else { -+ item["attributes"]["declaredInline"] = "0"; -+ } -+ item["attributes"]["funcName"] = d.funcNameAttr().getValue().str().c_str(); -+ auto ®ion = d.getRegion(); -+ size_t bbIdx = 0; -+ for (auto &b : region) { -+ string blockStr = "block" + std::to_string(bbIdx++); -+ uint64_t bbAddress = 0; -+ size_t opIdx = 0; -+ for (auto &inst : b) { -+ if (isa(inst)) { -+ continue; -+ } else if (isa(inst)) { -+ continue; -+ } else if (isa(inst)) { -+ continue; -+ } else if (isa(inst)) { -+ continue; -+ } -+ string opStr = "Operation" + std::to_string(opIdx++); -+ item["region"][blockStr]["ops"][opStr] = OperationJsonSerialize(&inst, bbAddress); -+ } -+ assert(bbAddress != 0); -+ item["region"][blockStr]["address"] = std::to_string(bbAddress); -+ } -+ operation = "FunctionOp" + std::to_string(i++); -+ root[operation] = item; -+ item.clear(); -+ } -+ out = root.toStyledString(); -+} -+ -+Json::Value PluginJson::OperationJsonSerialize(mlir::Operation *operation, uint64_t &bbId) -+{ -+ Json::Value root; -+ if (AssignOp op = llvm::dyn_cast(operation)) { -+ root = AssignOpJsonSerialize(op); -+ } else if (CallOp op = llvm::dyn_cast(operation)) { -+ root = CallOpJsonSerialize(op); -+ } else if (CondOp op = llvm::dyn_cast(operation)) { -+ root = CondOpJsonSerialize(op, bbId); -+ } else if (PhiOp op = llvm::dyn_cast(operation)) { -+ root = PhiOpJsonSerialize(op); -+ } else if (FallThroughOp op = llvm::dyn_cast(operation)) { -+ root = FallThroughOpJsonSerialize(op, bbId); -+ } else if (RetOp op = llvm::dyn_cast(operation)) { -+ root = RetOpJsonSerialize(op, bbId); -+ } else if (BaseOp op = llvm::dyn_cast(operation)) { -+ root = BaseOpJsonSerialize(op); -+ } -+ root["OperationName"] = operation->getName().getStringRef().str(); -+ return root; -+} -+ -+Json::Value PluginJson::BaseOpJsonSerialize(BaseOp data) -+{ -+ Json::Value root; -+ root["id"] = std::to_string(data.idAttr().getInt()); -+ root["opCode"] = data.opCodeAttr().getValue().str().c_str(); -+ return root; -+} -+ -+Json::Value PluginJson::RetOpJsonSerialize(RetOp data, uint64_t &bbId) -+{ -+ Json::Value root; -+ bbId = data.addressAttr().getInt(); -+ root["address"] = std::to_string(bbId); -+ return root; -+} -+ -+Json::Value PluginJson::FallThroughOpJsonSerialize(FallThroughOp data, uint64_t &bbId) -+{ -+ Json::Value root; -+ bbId = data.addressAttr().getInt(); -+ root["address"] = std::to_string(bbId); -+ root["destaddr"] = std::to_string(data.destaddrAttr().getInt()); -+ return root; -+} -+ -+void PluginJson::LocalDeclsJsonSerialize(vector& decls, string& out) -+{ -+ Json::Value root; -+ Json::Value operationObj; -+ Json::Value item; -+ int i = 0; -+ string operation; -+ -+ for (auto& decl: decls) { -+ item["id"] = std::to_string(decl.idAttr().getInt()); -+ item["attributes"]["symName"] = decl.symNameAttr().getValue().str().c_str(); -+ item["attributes"]["typeID"] = decl.typeIDAttr().getInt(); -+ item["attributes"]["typeWidth"] = decl.typeWidthAttr().getInt(); -+ operation = "localDecl" + std::to_string(i++); -+ root[operation] = item; -+ item.clear(); -+ } -+ out = root.toStyledString(); -+} -+ -+void PluginJson::LoopOpsJsonSerialize(vector& loops, string& out) -+{ -+ Json::Value root; -+ Json::Value operationObj; -+ Json::Value item; -+ int i = 0; -+ string operation; -+ -+ for (auto& loop: loops) { -+ item["id"] = std::to_string(loop.idAttr().getInt()); -+ item["index"] = std::to_string(loop.indexAttr().getInt()); -+ item["attributes"]["innerLoopId"] = std::to_string(loop.innerLoopIdAttr().getInt()); -+ item["attributes"]["outerLoopId"] = std::to_string(loop.outerLoopIdAttr().getInt()); -+ item["attributes"]["numBlock"] = std::to_string(loop.numBlockAttr().getInt()); -+ operation = "loopOp" + std::to_string(i++); -+ root[operation] = item; -+ item.clear(); -+ } -+ out = root.toStyledString(); -+} -+ -+void PluginJson::LoopOpJsonSerialize(mlir::Plugin::LoopOp& loop, string& out) -+{ -+ Json::Value root; -+ root["id"] = std::to_string(loop.idAttr().getInt()); -+ root["index"] = std::to_string(loop.indexAttr().getInt()); -+ root["attributes"]["innerLoopId"] = std::to_string(loop.innerLoopIdAttr().getInt()); -+ root["attributes"]["outerLoopId"] = std::to_string(loop.outerLoopIdAttr().getInt()); -+ root["attributes"]["numBlock"] = std::to_string(loop.numBlockAttr().getInt()); -+ out = root.toStyledString(); -+} -+ -+void PluginJson::BlocksJsonSerialize(vector& blocks, string& out) -+{ -+ Json::Value root; -+ Json::Value item; -+ int i = 0; -+ string index; -+ -+ for (auto& block : blocks) { -+ item["id"] = std::to_string(block); -+ index = "block" + std::to_string(i++); -+ root[index] = item; -+ item.clear(); -+ } -+ out = root.toStyledString(); -+} -+ -+void PluginJson::EdgesJsonSerialize(vector >& edges, string& out) -+{ -+ Json::Value root; -+ Json::Value item; -+ int i = 0; -+ string index; -+ -+ for (auto& edge : edges) { -+ item["src"] = std::to_string(edge.first); -+ item["dest"] = std::to_string(edge.second); -+ index = "edge" + std::to_string(i++); -+ root[index] = item; -+ item.clear(); -+ } -+ out = root.toStyledString(); -+} -+ -+void PluginJson::EdgeJsonSerialize(std::pair& edge, string& out) -+{ -+ Json::Value root; -+ root["src"] = std::to_string(edge.first); -+ root["dest"] = std::to_string(edge.second); -+ out = root.toStyledString(); -+} -+ -+// void类型的Json序列化 -+void PluginJson::NopJsonSerialize(string& out) -+{ -+ Json::Value root; -+ out = root.toStyledString(); -+} -+ -+void PluginJson::GetPhiOpsJsonSerialize(vector phiOps, string & out) -+{ -+ Json::Value root; -+ Json::Value item; -+ int i = 0; -+ string operation; -+ uint64_t placeholder = 0; -+ for (auto phi : phiOps) { -+ item = OperationJsonSerialize(phi.getOperation(), placeholder); -+ operation = "operation" + std::to_string(i++); -+ root[operation] = item; -+ item.clear(); -+ } -+ out = root.toStyledString(); -+} -+ -+Json::Value PluginJson::CallOpJsonSerialize(CallOp& data) -+{ -+ Json::Value item; -+ item["id"] = std::to_string(data.idAttr().getInt()); -+ item["callee"] = data.callee().str(); -+ item["OperationName"] = data.getOperation()->getName().getStringRef().str(); -+ size_t opIdx = 0; -+ for (mlir::Value v : data.getArgOperands()) { -+ string input = "input" + std::to_string(opIdx++); -+ item["operands"][input] = ValueJsonSerialize(v); -+ } -+ -+ return item; -+} -+ -+Json::Value PluginJson::CondOpJsonSerialize(CondOp& data, uint64_t &bbId) -+{ -+ Json::Value item; -+ item["id"] = std::to_string(data.idAttr().getInt()); -+ item["condCode"] = std::to_string(data.condCodeAttr().getInt()); -+ item["lhs"] = ValueJsonSerialize(data.GetLHS()); -+ item["rhs"] = ValueJsonSerialize(data.GetRHS()); -+ bbId = data.addressAttr().getInt(); -+ item["address"] = std::to_string(bbId); -+ item["tbaddr"] = std::to_string(data.tbaddrAttr().getInt()); -+ item["fbaddr"] = std::to_string(data.fbaddrAttr().getInt()); -+ return item; -+} -+ -+Json::Value PluginJson::PhiOpJsonSerialize(PhiOp& data) -+{ -+ Json::Value item; -+ item["id"] = std::to_string(data.idAttr().getInt()); -+ item["capacity"] = std::to_string(data.capacityAttr().getInt()); -+ item["nArgs"] = std::to_string(data.nArgsAttr().getInt()); -+ item["OperationName"] = data.getOperation()->getName().getStringRef().str(); -+ size_t opIdx = 0; -+ for (mlir::Value v : data.operands()) { -+ string input = "input" + std::to_string(opIdx++); -+ item["operands"][input] = ValueJsonSerialize(v); -+ } -+ -+ return item; -+} -+ -+Json::Value PluginJson::SSAOpJsonSerialize(SSAOp& data) -+{ -+ Json::Value item; -+ item["id"] = std::to_string(data.idAttr().getInt()); -+ item["defCode"] = std::to_string(data.defCodeAttr().getInt()); -+ item["readOnly"] = std::to_string(data.readOnlyAttr().getValue()); -+ item["nameVarId"] = std::to_string(data.nameVarIdAttr().getInt()); -+ item["ssaParmDecl"] = std::to_string(data.ssaParmDeclAttr().getInt()); -+ item["version"] = std::to_string(data.versionAttr().getInt()); -+ item["definingId"] = std::to_string(data.definingIdAttr().getInt()); -+ auto retTy = data.getResultType().dyn_cast(); -+ item["retType"] = TypeJsonSerialize(retTy); -+ return item; -+} -+ -+Json::Value PluginJson::AssignOpJsonSerialize(AssignOp& data) -+{ -+ Json::Value item; -+ item["id"] = std::to_string(data.idAttr().getInt()); -+ item["exprCode"] = std::to_string(data.exprCodeAttr().getInt()); -+ item["OperationName"] = data.getOperation()->getName().getStringRef().str(); -+ size_t opIdx = 0; -+ for (mlir::Value v : data.operands()) { -+ string input = "input" + std::to_string(opIdx++); -+ item["operands"][input] = ValueJsonSerialize(v); -+ } -+ -+ return item; -+} -+ -+Json::Value PluginJson::ValueJsonSerialize(mlir::Value data) -+{ -+ Json::Value root; -+ if (ConstOp cOp = data.getDefiningOp()) { -+ auto retTy = data.getType().dyn_cast(); -+ root["retType"] = TypeJsonSerialize(retTy); -+ root["id"] = std::to_string(cOp.idAttr().getInt()); -+ root["defCode"] = std::to_string(static_cast(IDefineCode::IntCST)); -+ root["value"] = std::to_string(cOp.initAttr().cast().getInt()); -+ } else if (MemOp mOp = data.getDefiningOp()) { -+ root = MemOpJsonSerialize(mOp); -+ } else if (SSAOp sOp = data.getDefiningOp()) { -+ root = SSAOpJsonSerialize(sOp); -+ } else if (PlaceholderOp phOp = data.getDefiningOp()) { -+ root["id"] = std::to_string(phOp.idAttr().getInt()); -+ root["defCode"] = std::to_string(phOp.defCodeAttr().getInt()); -+ auto retTy = phOp.getResultType().dyn_cast(); -+ root["retType"] = TypeJsonSerialize(retTy); -+ } else { -+ LOGE("ERROR: Can't Serialize!"); -+ } -+ return root; -+} -+ -+Json::Value PluginJson::MemOpJsonSerialize(MemOp& data) -+{ -+ Json::Value root; -+ root["id"] = std::to_string(data.idAttr().getInt()); -+ root["defCode"] = std::to_string(data.defCodeAttr().getInt()); -+ root["readOnly"] = std::to_string(data.readOnlyAttr().getValue()); -+ mlir::Value base = data.GetBase(); -+ mlir::Value offset = data.GetOffset(); -+ root["base"] = ValueJsonSerialize(base); -+ root["offset"] = ValueJsonSerialize(offset); -+ auto retTy = data.getResultType().dyn_cast(); -+ root["retType"] = TypeJsonSerialize(retTy); -+ return root; -+} -+ -+void PluginJson::IntegerSerialize(int64_t data, string& out) -+{ -+ Json::Value root; -+ root["integerData"] = data; -+ out = root.toStyledString(); -+} -+ -+void PluginJson::StringSerialize(const string& data, string& out) -+{ -+ Json::Value root; -+ root["stringData"] = data; -+ out = root.toStyledString(); -+} -+} // namespace PinClient --- -2.27.0.windows.1 - diff --git a/0004-Refactoring-Code-refactoring-of-Communication-Subsys.patch b/0004-Refactoring-Code-refactoring-of-Communication-Subsys.patch deleted file mode 100644 index 9492f1d..0000000 --- a/0004-Refactoring-Code-refactoring-of-Communication-Subsys.patch +++ /dev/null @@ -1,2397 +0,0 @@ -From 62597c33d89c967f7d4a4255699cd12a91b42a91 Mon Sep 17 00:00:00 2001 -From: Mingchuan Wu -Date: Tue, 7 Feb 2023 19:38:52 +0800 -Subject: [PATCH 4/9] [Refactoring] Code refactoring of Communication Subsystem - [3/3]. Code refactoring of communication subsystem. - - -diff --git a/CMakeLists.txt b/CMakeLists.txt -index d688fdf..fcd737b 100644 ---- a/CMakeLists.txt -+++ b/CMakeLists.txt -@@ -89,9 +89,12 @@ add_subdirectory(include) - add_subdirectory(lib) - - add_library(pin_gcc_client SHARED -+ "lib/gccPlugin/gccPlugin.cpp" - "lib/PluginClient/PluginLog.cpp" -+ "lib/PluginClient/PluginJson.cpp" -+ "lib/PluginClient/PluginInputCheck.cpp" -+ "lib/PluginClient/PluginGrpcPort.cpp" - "lib/PluginClient/PluginClient.cpp" -- "lib/IRTrans/IRTransPlugin.cpp" - ) - - target_link_libraries(pin_gcc_client -diff --git a/configs/pin-gcc-client.json b/configs/pin-gcc-client.json -index 390f8bf..bd0ff0c 100755 ---- a/configs/pin-gcc-client.json -+++ b/configs/pin-gcc-client.json -@@ -1,10 +1,10 @@ - { -- //server路径 -+ "//" : "server路径", - "path" : "/usr/local/bin/pin_server", - -- //libuser.so sha256文件名 -+ "//" : "libuser.so sha256文件名", - "sha256file" : "/usr/local/lib/libpin_user.sha256", - -- //超时时间,单位ms -- "timeout" : 200, -+ "//" : "超时时间,单位ms", -+ "timeout" : 200 - } -\ No newline at end of file -diff --git a/include/Dialect/PluginDialect.h b/include/Dialect/PluginDialect.h -index c5d3ad8..8cdb593 100644 ---- a/include/Dialect/PluginDialect.h -+++ b/include/Dialect/PluginDialect.h -@@ -16,11 +16,11 @@ - - - */ --//===----------------------------------------------------------------------===// --// -+// ===----------------------------------------------------------------------===// -+// - // This is the header file for the Plugin dialect. - // --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - - #ifndef PLUGIN_DIALECT_H - #define PLUGIN_DIALECT_H -diff --git a/include/Dialect/PluginOps.h b/include/Dialect/PluginOps.h -index 25089fe..a2fb310 100644 ---- a/include/Dialect/PluginOps.h -+++ b/include/Dialect/PluginOps.h -@@ -16,11 +16,11 @@ - - - */ --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // --// This is the header file for operations in Plugin dialect. -+// This is the header file for operations in Plugin dialect. - // --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - - #ifndef Plugin_OPS_H - #define Plugin_OPS_H -diff --git a/include/Dialect/PluginTypes.h b/include/Dialect/PluginTypes.h -index 60ba258..157b868 100644 ---- a/include/Dialect/PluginTypes.h -+++ b/include/Dialect/PluginTypes.h -@@ -74,13 +74,13 @@ private: - unsigned size; - }; // class PluginTypeBase - --namespace detail { -+namespace Detail { - struct PluginIntegerTypeStorage; - struct PluginFloatTypeStorage; - struct PluginPointerTypeStorage; - } - --class PluginIntegerType : public Type::TypeBase { -+class PluginIntegerType : public Type::TypeBase { - public: - using Base::Base; - -@@ -104,7 +104,7 @@ public: - SignednessSemantics getSignedness() const; - }; - --class PluginFloatType : public Type::TypeBase { -+class PluginFloatType : public Type::TypeBase { - public: - using Base::Base; - -@@ -115,7 +115,7 @@ public: - unsigned getWidth() const; - }; - --class PluginPointerType : public Type::TypeBase { -+class PluginPointerType : public Type::TypeBase { - public: - using Base::Base; - -@@ -133,7 +133,6 @@ public: - using Base::Base; - - PluginTypeID getPluginTypeID (); -- - }; // class PluginVoidType - - class PluginUndefType : public Type::TypeBase { -@@ -141,7 +140,6 @@ public: - using Base::Base; - - PluginTypeID getPluginTypeID (); -- - }; // class PluginUndefType - - class PluginBooleanType : public Type::TypeBase { -@@ -149,7 +147,6 @@ public: - using Base::Base; - - PluginTypeID getPluginTypeID (); -- - }; // class PluginBooleanType - - } // namespace PluginIR -diff --git a/include/PluginAPI/BasicPluginOpsAPI.h b/include/PluginAPI/BasicPluginOpsAPI.h -index 5eb1eed..8c47c58 100644 ---- a/include/PluginAPI/BasicPluginOpsAPI.h -+++ b/include/PluginAPI/BasicPluginOpsAPI.h -@@ -43,6 +43,12 @@ public: - BasicPluginOpsAPI() = default; - virtual ~BasicPluginOpsAPI() = default; - -+ virtual string GetDeclSourceFile(uint64_t gccDataAddr) = 0; -+ virtual string VariableName(int64_t gccDataAddr) = 0; -+ virtual string FuncName(int64_t gccDataAddr) = 0; -+ virtual int GetDeclSourceLine(uint64_t gccDataAddr) = 0; -+ virtual int GetDeclSourceColumn(uint64_t gccDataAddr) = 0; -+ - virtual uint64_t CreateBlock(uint64_t, uint64_t) = 0; - virtual void DeleteBlock(uint64_t, uint64_t) = 0; - virtual void SetImmediateDominator(uint64_t, uint64_t, uint64_t) = 0; -diff --git a/include/PluginAPI/PluginClientAPI.h b/include/PluginAPI/PluginClientAPI.h -index cb81986..07d6e52 100644 ---- a/include/PluginAPI/PluginClientAPI.h -+++ b/include/PluginAPI/PluginClientAPI.h -@@ -33,6 +33,12 @@ public: - PluginClientAPI () = default; - ~PluginClientAPI () = default; - -+ string GetDeclSourceFile(uint64_t gccDataAddr) override; -+ string VariableName(int64_t gccDataAddr) override; -+ string FuncName(int64_t gccDataAddr) override; -+ int GetDeclSourceLine(uint64_t gccDataAddr) override; -+ int GetDeclSourceColumn(uint64_t gccDataAddr) override; -+ - uint64_t CreateBlock(uint64_t, uint64_t) override; - void DeleteBlock(uint64_t, uint64_t) override; - void SetImmediateDominator(uint64_t, uint64_t, uint64_t) override; -diff --git a/include/PluginClient/GrpcKey.def b/include/PluginClient/GrpcKey.def -new file mode 100755 -index 0000000..f996544 ---- /dev/null -+++ b/include/PluginClient/GrpcKey.def -@@ -0,0 +1,6 @@ -+/* Definition of GRPC key name */ -+ -+GRPC_KEY(START, "start") -+GRPC_KEY(STOP, "stop") -+GRPC_KEY(USERFUNC, "userFunc") -+GRPC_KEY(INJECT, "injectPoint") -diff --git a/include/PluginClient/GrpcValue.def b/include/PluginClient/GrpcValue.def -new file mode 100755 -index 0000000..d9b391b ---- /dev/null -+++ b/include/PluginClient/GrpcValue.def -@@ -0,0 +1,6 @@ -+/* Definition of GRPC value */ -+ -+GRPC_VALUE(START_VALUE, "ok") -+GRPC_VALUE(STOP_VALUE, "ok") -+GRPC_VALUE(USERFUNC_VALUE, "execution completed") -+GRPC_VALUE(FINISH_VALUE, "finished") -diff --git a/include/PluginClient/JsonKey.def b/include/PluginClient/JsonKey.def -new file mode 100755 -index 0000000..adbd27e ---- /dev/null -+++ b/include/PluginClient/JsonKey.def -@@ -0,0 +1,5 @@ -+/* Definition of JsonKey */ -+ -+JSON_KEY(PATH, "path") -+JSON_KEY(SHA256, "sha256file") -+JSON_KEY(TIMEOUT, "timeout") -diff --git a/include/PluginClient/PassValue.def b/include/PluginClient/PassValue.def -new file mode 100755 -index 0000000..b9090ea ---- /dev/null -+++ b/include/PluginClient/PassValue.def -@@ -0,0 +1,5 @@ -+/* Definition of PASS value */ -+ -+PASS_VALUE(PASS_NAME, "refPassName") -+PASS_VALUE(PASS_NUM, "passNum") -+PASS_VALUE(PASS_POSITION, "passPosition") -diff --git a/include/PluginClient/PluginClient.h b/include/PluginClient/PluginClient.h -index a0137b5..89d7573 100644 ---- a/include/PluginClient/PluginClient.h -+++ b/include/PluginClient/PluginClient.h -@@ -18,35 +18,26 @@ - Create: 2022-08-18 - Description: - This file contains the declaration of the PluginClient class. -+ 主要完成功能:完成和server之间grpc通信及数据解析,获取gcc插件数据并进行IR转换,完成 -+ gcc注册点注入及参数保存。提供GetInstance获取client对象唯一实例,完成插件初始化并启动 -+ server子进程,处理超时异常事件 - */ - - #ifndef PLUGIN_CLIENT_H - #define PLUGIN_CLIENT_H - --#include --#include --#include --#include --#include --#include -- - #include "Dialect/PluginOps.h" - #include "Dialect/PluginTypes.h" - #include - #include "plugin.grpc.pb.h" --#include "gcc-plugin.h" - #include "PluginAPI/PluginClientAPI.h" -+#include "PluginClient/PluginGrpcPort.h" -+#include "PluginClient/PluginInputCheck.h" -+#include "PluginClient/PluginJson.h" - #include "PluginClient/PluginLog.h" - #include "Dialect/PluginDialect.h" - - namespace PinClient { --using std::cout; --using std::string; --using std::endl; --using std::vector; --using std::map; --using std::pair; -- - using plugin::PluginService; - using grpc::Channel; - using grpc::ClientContext; -@@ -55,6 +46,45 @@ using grpc::Status; - using plugin::ClientMsg; - using plugin::ServerMsg; - -+enum Grpckey { -+#define GRPC_KEY(KEY, NAME) KEY, -+#include "GrpcKey.def" -+#undef GRPC_KEY -+MAX_GRPC_KEYS -+}; -+ -+#define GRPC_KEY(KEY, NAME) NAME, -+const char *const grpckey[] = { -+#include "GrpcKey.def" -+}; -+#undef GRPC_KEY -+ -+enum GrpcValue { -+#define GRPC_VALUE(KEY, NAME) KEY, -+#include "GrpcValue.def" -+#undef GRPC_VALUE -+MAX_GRPC_VALUES -+}; -+ -+#define GRPC_VALUE(KEY, NAME) NAME, -+const char *const grpcValue[] = { -+#include "GrpcValue.def" -+}; -+#undef GRPC_VALUE -+ -+enum PassValue { -+#define PASS_VALUE(KEY, NAME) KEY, -+#include "PassValue.def" -+#undef PASS_VALUE -+MAX_PASS_VALUES -+}; -+ -+#define PASS_VALUE(KEY, NAME) NAME, -+const char *const passValue[] = { -+#include "PassValue.def" -+}; -+#undef PASS_VALUE -+ - enum InjectPoint : uint8_t { - HANDLE_PARSE_TYPE = 0, - HANDLE_PARSE_DECL, -@@ -68,8 +98,10 @@ enum InjectPoint : uint8_t { - HANDLE_AFTER_ALL_PASS, - HANDLE_COMPILE_END, - HANDLE_MANAGER_SETUP, -+ HANDLE_INCLUDE_FILE, - HANDLE_MAX, - }; -+ - typedef enum { - STATE_WAIT_BEGIN = 0, - STATE_BEGIN, -@@ -101,50 +133,17 @@ struct ManagerSetupData { - - class PluginClient { - public: -- PluginClient() = default; -- ~PluginClient() = default; -- PluginClient(std::shared_ptr channel) : serviceStub(PluginService::NewStub(channel)) {} - /* 定义的grpc服务端和客户端通信的接口函数 */ - void ReceiveSendMsg(const string& attribute, const string& value); - /* 获取client对象实例,有且只有一个实例对象 */ -- static std::shared_ptr GetInstance(void); -- void OpJsonSerialize(vector& data, string& out); -- void LoopOpsJsonSerialize(vector& loops, string& out); -- void LoopOpJsonSerialize(mlir::Plugin::LoopOp& loop, string& out); -- void BlocksJsonSerialize(vector&, string&); -- void EdgesJsonSerialize(vector >&, string&); -- void EdgeJsonSerialize(pair&, string&); -- void NopJsonSerialize(string&); -- void FunctionOpJsonSerialize(vector& data, string& out); -- void LocalDeclsJsonSerialize(vector& decls, string& out); -- void GetPhiOpsJsonSerialize(vector phiOps, string& out); -- Json::Value OperationJsonSerialize(mlir::Operation *, uint64_t&); -- Json::Value CallOpJsonSerialize(mlir::Plugin::CallOp& data); -- Json::Value CondOpJsonSerialize(mlir::Plugin::CondOp& data, uint64_t&); -- Json::Value PhiOpJsonSerialize(mlir::Plugin::PhiOp& data); -- Json::Value AssignOpJsonSerialize(mlir::Plugin::AssignOp& data); -- Json::Value BaseOpJsonSerialize(mlir::Plugin::BaseOp data); -- Json::Value FallThroughOpJsonSerialize(mlir::Plugin::FallThroughOp data, uint64_t&); -- Json::Value RetOpJsonSerialize(mlir::Plugin::RetOp data, uint64_t&); -- Json::Value ValueJsonSerialize(mlir::Value value); -- Json::Value MemOpJsonSerialize(mlir::Plugin::MemOp& data); -- Json::Value SSAOpJsonSerialize(mlir::Plugin::SSAOp& data); -- /* 将Type类型数据序列化 */ -- Json::Value TypeJsonSerialize(PluginIR::PluginTypeBase& type); -- PluginIR::PluginTypeBase TypeJsonDeSerialize(const string& data, mlir::MLIRContext &context); -+ static PluginClient *GetInstance(); - /* 获取gcc插件数据并进行IR转换,将转换后的数据序列化返回给server。param:函数入参序列化后的数据 */ -- void IRTransBegin(const string& funname, const string& param); -- /* 从配置文件读取初始化信息 */ -- static int GetInitInfo(string& serverPath, string& shaPath, int& timeout); -- /* 进行sha256校验 */ -- static int CheckSHA256(const string& shaPath); -- static void CheckSafeCompileFlag(const string& argName, const string& param); -- /* 解析gcc编译时传递的-fplugin-arg参数 */ -- static void GetArg(struct plugin_name_args *pluginInfo, string& serverPath, string& arg, LogPriority& logLevel); -+ void GetIRTransResult(void *gccData, const string& funname, const string& param); -+ void GetGccData(const string& funcName, const string& param, string& key, string& result); - /* 将服务端传递的InjectPoint转换为plugin_event */ - static int GetEvent(InjectPoint inject, plugin_event *event); -- static unsigned short FindUnusedPort(void); // 查找未被使用的端口号,确保并发情况下server和client一对一 -- UserFuncStateEnum GetUserFuncState(void) -+ void Init(struct plugin_name_args *pluginInfo, const string& pluginName, pid_t& serverPid); -+ UserFuncStateEnum GetUserFuncState() - { - return userFuncState; - } -@@ -168,10 +167,6 @@ public: - { - pluginAPIParams = name; - } -- void SetTimeout(int time) -- { -- timeout = time; -- } - void SetPluginName(const string& pluginName) - { - this->pluginName = pluginName; -@@ -188,19 +183,11 @@ public: - { - return injectFlag; - } -- void SetGrpcPort(unsigned short port) -- { -- grpcPort = port; -- } -- unsigned short GetGrpcPort(void) -- { -- return grpcPort; -- } -- bool TimerInit(void); -+ bool TimerInit(clockid_t id); - void TimerStart(int interval); - /* 保存注入点和函数名信息,value格式为 注入点:函数名称 */ - int AddRegisteredUserFunc(const string& value); -- map>& GetRegisteredUserFunc(void) -+ map>& GetRegisteredUserFunc() - { - return registeredUserFunc; - } -@@ -210,13 +197,24 @@ public: - } - /* grpc消息处理函数 */ - void ServerMsgProc(const string& attribute, const string& value); -- /* grpc的server被client拉起之前将port记录在/tmp/grpc_ports_pin_client.txt中, server和client建立通信后从文件中删除port,避免多进程时端口冲突 -- 文件若不存在,先创建文件 */ -- static int OpenLockFile(const char *path); -- /* 读取文件中保存的grpc端口号 */ -- static void ReadPortsFromLockFile(int fd, string& grpcPorts); -- /* server启动异常或者grpc建立通信后,将文件中记录的端口号删除 */ -- static bool DeletePortFromLockFile(unsigned short port); -+ int ClientStart(); -+ int ServerStart(pid_t& pid); // pid server线程pid -+ bool DeleteGrpcPort() -+ { -+ return grpcPort.DeletePortFromLockFile(); -+ } -+ bool GetStartFlag() -+ { -+ return startFlag; -+ } -+ void SetStartFlag(bool flag) -+ { -+ startFlag = flag; -+ } -+ PluginJson &GetJson(void) -+ { -+ return json; -+ } - - private: - std::unique_ptr serviceStub; // 保存grpc客户端stub对象 -@@ -225,18 +223,17 @@ private: - volatile UserFuncStateEnum userFuncState; - string pluginAPIName; // 保存用户调用PluginAPI的函数名 - string pluginAPIParams; // 保存用户调用PluginAPI函数的参数 -- int timeout; - timer_t timerId; - string pluginName; // 向gcc插件注册回调函数时需要 - bool injectFlag; // 是否完成将注册点信息注册到gcc -- unsigned short grpcPort; // server和client使用的端口号 -+ PluginGrpcPort grpcPort; -+ PluginInputCheck input; -+ PluginJson json; - /* 保存注册点和函数信息 */ - map> registeredUserFunc; -+ std::shared_ptr grpcChannel; -+ bool startFlag; - }; -- --/* pid:子进程server返回的pid,port:查找到的未使用的端口号 */ --int ServerStart(int timeout, const string& serverPath, pid_t& pid, string& port, const LogPriority logLevel); --int ClientStart(int timeout, const string& arg, const string& pluginName, const string& port); - } // namespace PinClient - - #endif -diff --git a/include/PluginClient/PluginGrpcPort.h b/include/PluginClient/PluginGrpcPort.h -new file mode 100755 -index 0000000..cd06864 ---- /dev/null -+++ b/include/PluginClient/PluginGrpcPort.h -@@ -0,0 +1,73 @@ -+/* Copyright (c) 2022-2022 Huawei Technologies Co., Ltd. -+ -+ This program is free software; you can redistribute it and/or modify it -+ under the terms of the GNU General Public License as published by the -+ Free Software Foundation; either version 3, or (at your option) any -+ later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program; see the file COPYING3. If not see -+ . -+ -+ Author: Mingchuan Wu and Yancheng Li -+ Create: 2022-08-18 -+ Description: -+ This file contains the declaration of the PluginGrpcPort class. -+ 主要完成功能:查找未使用的端口号,并将端口号写入到共享文件中,通过文件锁控制多进程间访问,并提供 -+ DeletePortFromLockFile接口在退出时删除写入的端口号 -+*/ -+ -+#ifndef PLUGIN_GRPC_PORT_H -+#define PLUGIN_GRPC_PORT_H -+ -+#include -+ -+namespace PinClient { -+using std::string; -+ -+class PluginGrpcPort { -+public: -+ PluginGrpcPort() -+ { -+ const int startPort = 40000; -+ lockFilePath = "/tmp/grpc_ports_pin_client.txt"; -+ basePort = startPort; -+ port = 0; -+ } -+ bool FindUnusedPort(); // 查找未被使用的端口号,确保并发情况下server和client一对一 -+ /* grpc的server被client拉起之前将port记录在/tmp/grpc_ports_pin_client.txt中, server和client建立通信后从文件中删除port,避免多进程时端口冲突 -+ 文件若不存在,先创建文件 */ -+ int OpenFile(const char *path); -+ /* 读取文件中保存的grpc端口号 */ -+ bool ReadPortsFromLockFile(int fd, string& grpcPorts); -+ /* server启动异常或者grpc建立通信后,将文件中记录的端口号删除 */ -+ bool DeletePortFromLockFile(); -+ unsigned short GetPort() -+ { -+ return port; -+ } -+ void SetLockFilePath(const string& path) -+ { -+ lockFilePath = path; -+ } -+ void SetBasePort(unsigned short port) -+ { -+ basePort = port; -+ } -+ unsigned short GetBasePort() -+ { -+ return basePort; -+ } -+ -+private: -+ unsigned short port; // server和client使用的端口号 -+ string lockFilePath; -+ unsigned short basePort; -+}; -+} // namespace PinClient -+#endif -\ No newline at end of file -diff --git a/include/PluginClient/PluginInputCheck.h b/include/PluginClient/PluginInputCheck.h -new file mode 100755 -index 0000000..cbbfc3f ---- /dev/null -+++ b/include/PluginClient/PluginInputCheck.h -@@ -0,0 +1,136 @@ -+/* Copyright (c) 2022-2022 Huawei Technologies Co., Ltd. -+ -+ This program is free software; you can redistribute it and/or modify it -+ under the terms of the GNU General Public License as published by the -+ Free Software Foundation; either version 3, or (at your option) any -+ later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program; see the file COPYING3. If not see -+ . -+ -+ Author: Mingchuan Wu and Yancheng Li -+ Create: 2022-08-18 -+ Description: -+ This file contains the declaration of the PluginInputCheck class. -+ 主要完成功能:获取gcc编译时的参数,读取config.json配置文件,完成sha256校验,完成 -+ 安全编译选项参数检查,并保存serverPath,shaPath,timeout等信息 -+*/ -+ -+#ifndef PLUGIN_INPUT_CHECK_H -+#define PLUGIN_INPUT_CHECK_H -+ -+#include -+#include -+#include -+#include "gcc-plugin.h" -+#include "PluginClient/PluginLog.h" -+ -+namespace PinClient { -+using std::map; -+using std::string; -+using std::vector; -+ -+enum Jsonkey { -+#define JSON_KEY(KEY, NAME) KEY, -+#include "JsonKey.def" -+#undef JSON_KEY -+MAX_JSON_KEYS -+}; -+ -+#define JSON_KEY(KEY, NAME) NAME, -+const char *const jsonkey[] = { -+#include "JsonKey.def" -+}; -+#undef JSON_KEY -+ -+enum { -+ KEY_SERVER_PATH, -+ KEY_LOG_LEVEL, -+}; -+ -+class PluginInputCheck { -+public: -+ const string shaFile = "/libpin_user.sha256"; -+ PluginInputCheck(); -+ /* 从配置文件读取初始化信息 */ -+ int GetInitInfo(); -+ int CheckServerFile(); // 对server文件进行检查 -+ int CheckShaFile(); // 对sha256校验文件进行检查 -+ bool ReadConfigfile(Json::Value& root); -+ /* 进行sha256校验 */ -+ int CheckSHA256(); -+ void CheckSafeCompileFlag(const string& argName, const string& param); -+ /* 解析gcc编译时传递的-fplugin-arg参数 */ -+ void GetInputArgs(struct plugin_name_args *pluginInfo); -+ int mapFind(const std::map& map, const string& key) -+ { -+ auto it = map.find(key); -+ if (it != map.end()) { -+ return it->second; -+ } -+ return -1; -+ } -+ string& GetArgs() -+ { -+ return args; -+ } -+ string& GetServerPath() -+ { -+ return serverPath; -+ } -+ void SetServerPath(const string& path) -+ { -+ serverPath = path; -+ } -+ LogPriority GetLogLevel() -+ { -+ return logLevel; -+ } -+ void SetLogLevel(LogPriority level) -+ { -+ logLevel = level; -+ } -+ string& GetShaPath() -+ { -+ return shaPath; -+ } -+ void SetShaPath(const string& path) -+ { -+ shaPath = path; -+ } -+ int GetTimeout() -+ { -+ return timeout; -+ } -+ void SetTimeout(int time); -+ void SetConfigPath(const string& path) -+ { -+ configFilePath = path; -+ } -+ string& GetConfigPath() -+ { -+ return configFilePath; -+ } -+ string GetServerDir() -+ { -+ int index = serverPath.find_last_of("/"); -+ return serverPath.substr(0, index); -+ } -+ -+private: -+ string args; -+ string serverPath; -+ string configFilePath; -+ LogPriority logLevel; -+ string shaPath; -+ int timeout; -+ vector safeCompileFlags; -+}; -+} // namespace PinClient -+#endif -\ No newline at end of file -diff --git a/include/PluginClient/PluginJson.h b/include/PluginClient/PluginJson.h -new file mode 100755 -index 0000000..91bd925 ---- /dev/null -+++ b/include/PluginClient/PluginJson.h -@@ -0,0 +1,70 @@ -+/* Copyright (c) 2022-2022 Huawei Technologies Co., Ltd. -+ -+ This program is free software; you can redistribute it and/or modify it -+ under the terms of the GNU General Public License as published by the -+ Free Software Foundation; either version 3, or (at your option) any -+ later version. -+ -+ This program is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with this program; see the file COPYING3. If not see -+ . -+ -+ Author: Mingchuan Wu and Yancheng Li -+ Create: 2022-08-18 -+ Description: -+ This file contains the declaration of the PluginJson class. -+ 主要完成功能:将operation、Decl、Type、Inter、string等类型数据序列化 -+*/ -+ -+#ifndef PLUGIN_JSON_H -+#define PLUGIN_JSON_H -+ -+#include -+#include -+ -+#include "Dialect/PluginDialect.h" -+#include "Dialect/PluginOps.h" -+#include "Dialect/PluginTypes.h" -+ -+namespace PinClient { -+using std::string; -+using std::vector; -+ -+class PluginJson { -+public: -+ void OpJsonSerialize(vector& data, string& out); -+ void LoopOpsJsonSerialize(vector& loops, string& out); -+ void LoopOpJsonSerialize(mlir::Plugin::LoopOp& loop, string& out); -+ void BlocksJsonSerialize(vector&, string&); -+ void EdgesJsonSerialize(vector >&, string&); -+ void EdgeJsonSerialize(std::pair&, string&); -+ void NopJsonSerialize(string&); -+ void FunctionOpJsonSerialize(vector& data, string& out); -+ void LocalDeclsJsonSerialize(vector& decls, string& out); -+ void GetPhiOpsJsonSerialize(vector phiOps, string& out); -+ Json::Value OperationJsonSerialize(mlir::Operation *, uint64_t&); -+ Json::Value CallOpJsonSerialize(mlir::Plugin::CallOp& data); -+ Json::Value CondOpJsonSerialize(mlir::Plugin::CondOp& data, uint64_t&); -+ Json::Value PhiOpJsonSerialize(mlir::Plugin::PhiOp& data); -+ Json::Value AssignOpJsonSerialize(mlir::Plugin::AssignOp& data); -+ Json::Value BaseOpJsonSerialize(mlir::Plugin::BaseOp data); -+ Json::Value FallThroughOpJsonSerialize(mlir::Plugin::FallThroughOp data, uint64_t&); -+ Json::Value RetOpJsonSerialize(mlir::Plugin::RetOp data, uint64_t&); -+ Json::Value ValueJsonSerialize(mlir::Value value); -+ Json::Value MemOpJsonSerialize(mlir::Plugin::MemOp& data); -+ Json::Value SSAOpJsonSerialize(mlir::Plugin::SSAOp& data); -+ /* 将Type类型数据序列化 */ -+ Json::Value TypeJsonSerialize(PluginIR::PluginTypeBase& type); -+ PluginIR::PluginTypeBase TypeJsonDeSerialize(const string& data, mlir::MLIRContext &context); -+ /* 将整数型数据序列化 */ -+ void IntegerSerialize(int64_t data, string& out); -+ /* 将字符串型数据序列化 */ -+ void StringSerialize(const string& data, string& out); -+}; -+} // namespace PinClient -+#endif -diff --git a/include/PluginClient/PluginLog.h b/include/PluginClient/PluginLog.h -index a91a1db..8fafa85 100644 ---- a/include/PluginClient/PluginLog.h -+++ b/include/PluginClient/PluginLog.h -@@ -18,14 +18,14 @@ - Create: 2022-08-18 - Description: - This file contains the declaration of the Plugin_Log class. -+ 主要完成功能:提供LOGE、LOGW、LOGI、LOGD四个log保存接口,并提供SetLogPriority接口 -+ 设置log级别 - */ - - #ifndef PLUGIN_LOG_H - #define PLUGIN_LOG_H - - namespace PinClient { --#define LOG_FILE_SIZE (10 * 1024 * 1024) -- - enum LogPriority : uint8_t { - PRIORITY_ERROR = 0, - PRIORITY_WARN, -@@ -33,12 +33,13 @@ enum LogPriority : uint8_t { - PRIORITY_DEBUG - }; - void LogPrint(LogPriority priority, const char *tag, const char *fmt, ...); --void CloseLog(void); -+void CloseLog(); - bool SetLogPriority(LogPriority priority); -+void SetLogFileSize(unsigned int size); // 设置log文件大小,默认为10M,当日志超过10M后,重新命名 - - #define LOGE(...) LogPrint(PRIORITY_ERROR, "ERROR:", __VA_ARGS__) - #define LOGW(...) LogPrint(PRIORITY_WARN, "WARN:", __VA_ARGS__) --#define LOGI(...) LogPrint(PRIORITY_INFO, "", __VA_ARGS__) -+#define LOGI(...) LogPrint(PRIORITY_INFO, "INFO:", __VA_ARGS__) - #define LOGD(...) LogPrint(PRIORITY_DEBUG, "DEBUG:", __VA_ARGS__) - } // namespace PinClient - -diff --git a/include/Translate/GimpleToPluginOps.h b/include/Translate/GimpleToPluginOps.h -index 689ece3..5f8bdf0 100644 ---- a/include/Translate/GimpleToPluginOps.h -+++ b/include/Translate/GimpleToPluginOps.h -@@ -36,18 +36,24 @@ - - namespace PluginIR { - using std::vector; -+using std::string; - using namespace mlir::Plugin; - --namespace detail { -+namespace Detail { - class BlockFromGimpleTranslatorImpl; - // class BlockToPluginIRTranslatorImpl; --} // namespace detail -+} // namespace Detail - - class GimpleToPluginOps { - public: - GimpleToPluginOps (mlir::MLIRContext &); - ~GimpleToPluginOps (); - -+ string DeclSourceFile(uint64_t gccDataAddr); -+ string GetVariableName(uint64_t gccDataAddr); -+ string GetFuncName(uint64_t gccDataAddr); -+ int DeclSourceLine(uint64_t gccDataAddr); -+ int DeclSourceColumn(uint64_t gccDataAddr); - /* ToPluginInterface */ - uint64_t CreateBlock(uint64_t, uint64_t); - void DeleteBlock(uint64_t, uint64_t); -@@ -107,7 +113,7 @@ private: - TypeToPluginIRTranslator pluginTypeTranslator; - - // process basic_block -- std::unique_ptr bbTranslator; -+ std::unique_ptr bbTranslator; - bool ProcessBasicBlock(intptr_t, Region&); - bool ProcessGimpleStmt(intptr_t, Region&); - }; -diff --git a/include/Translate/ToPluginOpsInterface.h b/include/Translate/ToPluginOpsInterface.h -deleted file mode 100644 -index 6d2b74b..0000000 ---- a/include/Translate/ToPluginOpsInterface.h -+++ /dev/null -@@ -1,59 +0,0 @@ --/* Copyright (c) 2022-2022 Huawei Technologies Co., Ltd. -- -- This program is free software; you can redistribute it and/or modify it -- under the terms of the GNU General Public License as published by the -- Free Software Foundation; either version 3, or (at your option) any -- later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program; see the file COPYING3. If not see -- . -- -- Author: Mingchuan Wu and Yancheng Li -- Create: 2022-08-18 -- Description: -- This file contains the declaration of the ToPluginInterface class --*/ -- --#ifndef TO_PLUGINOPS_INTERFACE_H --#define TO_PLUGINOPS_INTERFACE_H -- --#include --#include "Dialect/PluginOps.h" -- --namespace PluginIR { --using std::vector; --using namespace mlir::Plugin; -- --/* The ToPluginInterface class defines the plugin interfaces that different -- compilers need to implement. */ --class ToPluginOpsInterface { --public: -- /* Operation. */ -- virtual vector GetAllFunction() = 0; -- virtual vector GetAllDecls(uint64_t) = 0; -- virtual vector GetAllLoops(uint64_t) = 0; -- virtual LoopOp GetLoop(uint64_t) = 0; -- virtual bool IsBlockInside(uint64_t, uint64_t) = 0; -- virtual vector GetBlocksInLoop(uint64_t) = 0; -- virtual uint64_t AllocateNewLoop(void) = 0; -- virtual void DeleteLoop(uint64_t) = 0; -- virtual void AddLoop (uint64_t, uint64_t, uint64_t) = 0; -- virtual uint64_t GetHeader(uint64_t) = 0; -- virtual uint64_t GetLatch(uint64_t) = 0; -- virtual vector > GetLoopExits(uint64_t) = 0; -- virtual std::pair GetLoopSingleExit(uint64_t) = 0; -- virtual LoopOp GetBlockLoopFather(uint64_t) = 0; -- virtual bool UpdateSSA() = 0; -- virtual vector GetPhiOpsInsideBlock(uint64_t) = 0; -- virtual bool IsDomInfoAvailable() = 0; -- --}; --} // namespace PluginIR -- --#endif // TO_PLUGINOPS_INTERFACE_H -\ No newline at end of file -diff --git a/include/Translate/TypeTranslation.h b/include/Translate/TypeTranslation.h -index 964305c..98d279c 100644 ---- a/include/Translate/TypeTranslation.h -+++ b/include/Translate/TypeTranslation.h -@@ -36,10 +36,10 @@ namespace PluginIR { - using std::vector; - using namespace mlir; - --namespace detail { -+namespace Detail { - class TypeFromPluginIRTranslatorImpl; - class TypeToPluginIRTranslatorImpl; --} // namespace detail -+} // namespace Detail - - class TypeFromPluginIRTranslator { - public: -@@ -54,7 +54,7 @@ public: - uint64_t getBitWidth (PluginTypeBase type); - - private: -- std::unique_ptr impl; -+ std::unique_ptr impl; - }; - - class TypeToPluginIRTranslator { -@@ -65,7 +65,7 @@ public: - uintptr_t translateType (PluginTypeBase type); - - private: -- std::unique_ptr impl; -+ std::unique_ptr impl; - }; - - } // namespace PluginIR -diff --git a/include/IRTrans/IRTransPlugin.h b/include/gccPlugin/gccPlugin.h -similarity index 83% -rename from include/IRTrans/IRTransPlugin.h -rename to include/gccPlugin/gccPlugin.h -index 400e8e0..bb2e0d3 100755 ---- a/include/IRTrans/IRTransPlugin.h -+++ b/include/gccPlugin/gccPlugin.h -@@ -18,6 +18,8 @@ - Create: 2022-08-18 - Description: - This file contains the declaration of the RegisterPluginEvent. -+ 主要完成功能:提供RegisterPluginEvent和RegisterPassManagerSetup两个注册gcc -+ 回调点接口 - */ - - #ifndef IRTRANS_PLUGIN_H -@@ -26,6 +28,6 @@ - #include "PluginClient/PluginClient.h" - - int RegisterPluginEvent(PinClient::InjectPoint inject, const std::string& pluginName); --int RegisterPassManagerSetup(PinClient::InjectPoint inject, const PinClient::ManagerSetupData& setupData, -+void RegisterPassManagerSetup(unsigned int index, const PinClient::ManagerSetupData& setupData, - const std::string& pluginName); - #endif -diff --git a/lib/Dialect/PluginDialect.cpp b/lib/Dialect/PluginDialect.cpp -index 7071e64..527c076 100644 ---- a/lib/Dialect/PluginDialect.cpp -+++ b/lib/Dialect/PluginDialect.cpp -@@ -16,11 +16,11 @@ - - - */ --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // - // This file defines Plugin dialect. - // --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - - #include "Dialect/PluginDialect.h" - #include "Dialect/PluginOps.h" -@@ -29,20 +29,21 @@ - using namespace mlir; - using namespace mlir::Plugin; - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // Plugin dialect. --//===----------------------------------------------------------------------===// -- --void PluginDialect::initialize() { -- addTypes(); -- -- addOperations< -+// ===----------------------------------------------------------------------===// -+ -+void PluginDialect::initialize() -+{ -+ addTypes(); -+ -+ addOperations< - #define GET_OP_LIST - #include "Dialect/PluginOps.cpp.inc" -- >(); -+ >(); - } -\ No newline at end of file -diff --git a/lib/Dialect/PluginOps.cpp b/lib/Dialect/PluginOps.cpp -index ac2a4ee..052ebfd 100644 ---- a/lib/Dialect/PluginOps.cpp -+++ b/lib/Dialect/PluginOps.cpp -@@ -16,11 +16,11 @@ - - - */ --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // - // This file defines operations in the Plugin dialect. - // --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - - #include "Dialect/PluginDialect.h" - #include "Dialect/PluginOps.h" -@@ -33,7 +33,8 @@ using namespace mlir; - using namespace mlir::Plugin; - - void FunctionOp::build(mlir::OpBuilder &builder, mlir::OperationState &state, -- uint64_t id, StringRef funcName, bool declaredInline) { -+ uint64_t id, StringRef funcName, bool declaredInline) -+{ - FunctionOp::build(builder, state, - builder.getI64IntegerAttr(id), - builder.getStringAttr(funcName), -@@ -41,8 +42,8 @@ void FunctionOp::build(mlir::OpBuilder &builder, mlir::OperationState &state, - } - - void LocalDeclOp::build(mlir::OpBuilder &builder, mlir::OperationState &state, -- uint64_t id, StringRef symName, -- int64_t typeID, uint64_t typeWidth) { -+ uint64_t id, StringRef symName, int64_t typeID, uint64_t typeWidth) -+{ - LocalDeclOp::build(builder, state, - builder.getI64IntegerAttr(id), - builder.getStringAttr(symName), -@@ -51,8 +52,8 @@ void LocalDeclOp::build(mlir::OpBuilder &builder, mlir::OperationState &state, - } - - void LoopOp::build(mlir::OpBuilder &builder, mlir::OperationState &state, -- uint64_t id, uint32_t index, uint64_t innerLoopId, -- uint64_t outerLoopId, uint32_t numBlock) { -+ uint64_t id, uint32_t index, uint64_t innerLoopId, uint64_t outerLoopId, uint32_t numBlock) -+{ - LoopOp::build(builder, state, - builder.getI64IntegerAttr(id), - builder.getI32IntegerAttr(index), -@@ -61,12 +62,11 @@ void LoopOp::build(mlir::OpBuilder &builder, mlir::OperationState &state, - builder.getI32IntegerAttr(numBlock)); - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // PlaceholderOp - - void PlaceholderOp::build(OpBuilder &builder, OperationState &state, -- uint64_t id, IDefineCode defCode, bool readOnly, -- Type retType) -+ uint64_t id, IDefineCode defCode, bool readOnly, Type retType) - { - state.addAttribute("id", builder.getI64IntegerAttr(id)); - state.addAttribute("defCode", -@@ -75,12 +75,11 @@ void PlaceholderOp::build(OpBuilder &builder, OperationState &state, - state.addTypes(retType); - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // MemOp - - void MemOp::build(OpBuilder &builder, OperationState &state, -- uint64_t id, IDefineCode defCode, bool readOnly, -- Value addr, Value offset, Type retType) -+ uint64_t id, IDefineCode defCode, bool readOnly, Value addr, Value offset, Type retType) - { - state.addAttribute("id", builder.getI64IntegerAttr(id)); - state.addAttribute("readOnly", builder.getBoolAttr(readOnly)); -@@ -90,13 +89,11 @@ void MemOp::build(OpBuilder &builder, OperationState &state, - if (retType) state.addTypes(retType); - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // SSAOp - --void SSAOp::build(OpBuilder &builder, OperationState &state, uint64_t id, -- IDefineCode defCode, bool readOnly, uint64_t nameVarId, -- uint64_t ssaParmDecl, uint64_t version, -- uint64_t definingId, Type retType) -+void SSAOp::build(OpBuilder &builder, OperationState &state, uint64_t id, IDefineCode defCode, bool readOnly, -+ uint64_t nameVarId, uint64_t ssaParmDecl, uint64_t version, uint64_t definingId, Type retType) - { - state.addAttribute("id", builder.getI64IntegerAttr(id)); - state.addAttribute("defCode", -@@ -105,16 +102,15 @@ void SSAOp::build(OpBuilder &builder, OperationState &state, uint64_t id, - state.addAttribute("nameVarId", builder.getI64IntegerAttr(nameVarId)); - state.addAttribute("ssaParmDecl", builder.getI64IntegerAttr(ssaParmDecl)); - state.addAttribute("version", builder.getI64IntegerAttr(version)); -- state.addAttribute("definingId", builder.getI64IntegerAttr(definingId)); -+ state.addAttribute("definingId", builder.getI64IntegerAttr(definingId)); - state.addTypes(retType); - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // ConstOp - - void ConstOp::build(OpBuilder &builder, OperationState &state, uint64_t id, -- IDefineCode defCode, bool readOnly, Attribute init, -- Type retType) -+ IDefineCode defCode, bool readOnly, Attribute init, Type retType) - { - state.addAttribute("id", builder.getI64IntegerAttr(id)); - state.addAttribute("defCode", -@@ -124,12 +120,11 @@ void ConstOp::build(OpBuilder &builder, OperationState &state, uint64_t id, - if (retType) state.addTypes(retType); - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // PointerOp - - void PointerOp::build(OpBuilder &builder, OperationState &state, uint64_t id, -- IDefineCode defCode, bool readOnly, Type retType, -- bool pointeeReadOnly) -+ IDefineCode defCode, bool readOnly, Type retType, bool pointeeReadOnly) - { - state.addAttribute("id", builder.getI64IntegerAttr(id)); - state.addAttribute("defCode", -@@ -139,12 +134,11 @@ void PointerOp::build(OpBuilder &builder, OperationState &state, uint64_t id, - state.addAttribute("pointeeReadOnly", builder.getBoolAttr(pointeeReadOnly)); - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // CallOp - - void CallOp::build(OpBuilder &builder, OperationState &state, -- uint64_t id, StringRef callee, -- ArrayRef arguments, Type retType) -+ uint64_t id, StringRef callee, ArrayRef arguments, Type retType) - { - state.addAttribute("id", builder.getI64IntegerAttr(id)); - state.addOperands(arguments); -@@ -154,21 +148,25 @@ void CallOp::build(OpBuilder &builder, OperationState &state, - - /// Return the callee of the generic call operation, this is required by the - /// call interface. --CallInterfaceCallable CallOp::getCallableForCallee() { -+CallInterfaceCallable CallOp::getCallableForCallee() -+{ - return (*this)->getAttrOfType("callee"); - } - - /// Get the argument operands to the called function, this is required by the - /// call interface. --Operation::operand_range CallOp::getArgOperands() { return inputs(); } -+Operation::operand_range CallOp::getArgOperands() -+{ -+ return inputs(); -+} - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // CondOp - --void CondOp::build(OpBuilder &builder, OperationState &state, -- uint64_t id, uint64_t address, IComparisonCode condCode, -- Value lhs, Value rhs, Block* tb, Block* fb, uint64_t tbaddr, -- uint64_t fbaddr, Value trueLabel, Value falseLabel) { -+void CondOp::build(OpBuilder &builder, OperationState &state, uint64_t id, uint64_t address, -+ IComparisonCode condCode, Value lhs, Value rhs, Block* tb, Block* fb, uint64_t tbaddr, -+ uint64_t fbaddr, Value trueLabel, Value falseLabel) -+{ - state.addAttribute("id", builder.getI64IntegerAttr(id)); - state.addAttribute("address", builder.getI64IntegerAttr(address)); - state.addOperands({lhs, rhs}); -@@ -182,29 +180,29 @@ void CondOp::build(OpBuilder &builder, OperationState &state, - if (falseLabel != nullptr) state.addOperands(falseLabel); - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // PhiOp - --void PhiOp::build(OpBuilder &builder, OperationState &state, -- uint64_t id, uint32_t capacity, uint32_t nArgs, -- ArrayRef operands, Type resultType) { -+void PhiOp::build(OpBuilder &builder, OperationState &state, uint64_t id, uint32_t capacity, -+ uint32_t nArgs, ArrayRef operands, Type resultType) -+{ - state.addAttribute("id", builder.getI64IntegerAttr(id)); - state.addAttribute("capacity", builder.getI32IntegerAttr(capacity)); - state.addAttribute("nArgs", builder.getI32IntegerAttr(nArgs)); - state.addOperands(operands); -- if (resultType != nullptr) state.addTypes(resultType); -+ if (resultType != nullptr) { -+ state.addTypes(resultType); -+ } - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // AssignOp - --void AssignOp::build(OpBuilder &builder, OperationState &state, -- uint64_t id, IExprCode exprCode, -- ArrayRef operands, Type resultType) -+void AssignOp::build(OpBuilder &builder, OperationState &state, uint64_t id, -+ IExprCode exprCode, ArrayRef operands, Type resultType) - { - state.addAttribute("id", builder.getI64IntegerAttr(id)); -- state.addAttribute("exprCode", -- builder.getI32IntegerAttr(static_cast(exprCode))); -+ state.addAttribute("exprCode", builder.getI32IntegerAttr(static_cast(exprCode))); - state.addOperands(operands); - if (resultType != nullptr) state.addTypes(resultType); - } -@@ -216,47 +214,46 @@ void AssignOp::build(OpBuilder &builder, OperationState &state, - /// or `false` on success. This allows for easily chaining together a set of - /// parser rules. These rules are used to populate an `mlir::OperationState` - /// similarly to the `build` methods described above. --static mlir::ParseResult parseAssignOp(mlir::OpAsmParser &parser, -- mlir::OperationState &result) { -- mlir::DenseElementsAttr value; -- if (parser.parseOptionalAttrDict(result.attributes) || -- parser.parseAttribute(value, "value", result.attributes)) -- return failure(); -- -- result.addTypes(value.getType()); -- return success(); -+static mlir::ParseResult parseAssignOp(mlir::OpAsmParser &parser, mlir::OperationState &result) -+{ -+ mlir::DenseElementsAttr value; -+ if (parser.parseOptionalAttrDict(result.attributes) || -+ parser.parseAttribute(value, "value", result.attributes)) { -+ return failure(); -+ } -+ -+ result.addTypes(value.getType()); -+ return success(); - } - - /// The 'OpAsmPrinter' class is a stream that allows for formatting - /// strings, attributes, operands, types, etc. --static void print(mlir::OpAsmPrinter &printer, AssignOp op) { -- printer << "assign "; -- printer.printType(op.getType()); --// printer << op.value(); -+static void print(mlir::OpAsmPrinter &printer, AssignOp op) -+{ -+ printer << "assign "; -+ printer.printType(op.getType()); - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // BaseOp - --void BaseOp::build(OpBuilder &builder, OperationState &state, -- uint64_t id, StringRef opCode) -+void BaseOp::build(OpBuilder &builder, OperationState &state, uint64_t id, StringRef opCode) - { - state.addAttribute("id", builder.getI64IntegerAttr(id)); - state.addAttribute("opCode", builder.getStringAttr(opCode)); - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // FallThroughOp - --void FallThroughOp::build(OpBuilder &builder, OperationState &state, -- uint64_t address, Block* dest, uint64_t destaddr) -+void FallThroughOp::build(OpBuilder &builder, OperationState &state, uint64_t address, Block* dest, uint64_t destaddr) - { - state.addAttribute("address", builder.getI64IntegerAttr(address)); - state.addAttribute("destaddr", builder.getI64IntegerAttr(destaddr)); - state.addSuccessors(dest); - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // RetOp - - void RetOp::build(OpBuilder &builder, OperationState &state, uint64_t address) -@@ -264,9 +261,9 @@ void RetOp::build(OpBuilder &builder, OperationState &state, uint64_t address) - state.addAttribute("address", builder.getI64IntegerAttr(address)); - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // TableGen'd op method definitions --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - - #define GET_OP_CLASSES - #include "Dialect/PluginOps.cpp.inc" -\ No newline at end of file -diff --git a/lib/Dialect/PluginTypes.cpp b/lib/Dialect/PluginTypes.cpp -index edccdd5..396bf0f 100644 ---- a/lib/Dialect/PluginTypes.cpp -+++ b/lib/Dialect/PluginTypes.cpp -@@ -28,7 +28,7 @@ using namespace mlir; - using namespace PluginIR; - - namespace PluginIR { --namespace detail { -+namespace Detail { - /// Integer Type Storage and Uniquing. - struct PluginIntegerTypeStorage : public TypeStorage { - PluginIntegerTypeStorage(unsigned width, -@@ -38,16 +38,18 @@ namespace detail { - /// The hash key used for uniquing. - using KeyTy = std::pair; - -- static llvm::hash_code hashKey(const KeyTy &key) { -+ static llvm::hash_code hashKey(const KeyTy &key) -+ { - return llvm::hash_value(key); - } - -- bool operator==(const KeyTy &key) const { -+ bool operator==(const KeyTy &key) const -+ { - return KeyTy(width, signedness) == key; - } - -- static PluginIntegerTypeStorage *construct(TypeStorageAllocator &allocator, -- KeyTy key) { -+ static PluginIntegerTypeStorage *construct(TypeStorageAllocator &allocator, KeyTy key) -+ { - return new (allocator.allocate()) - PluginIntegerTypeStorage(key.first, key.second); - } -@@ -62,12 +64,13 @@ namespace detail { - /// The hash key used for uniquing. - using KeyTy = unsigned; - -- bool operator==(const KeyTy &key) const { -+ bool operator==(const KeyTy &key) const -+ { - return KeyTy(width) == key; - } - -- static PluginFloatTypeStorage *construct(TypeStorageAllocator &allocator, -- KeyTy key) { -+ static PluginFloatTypeStorage *construct(TypeStorageAllocator &allocator, KeyTy key) -+ { - return new (allocator.allocate()) - PluginFloatTypeStorage(key); - } -@@ -81,26 +84,27 @@ namespace detail { - PluginPointerTypeStorage(const KeyTy &key) - : pointee(std::get<0>(key)), readOnlyPointee(std::get<1>(key)) {} - -- static PluginPointerTypeStorage *construct(TypeStorageAllocator &allocator, -- KeyTy key) { -+ static PluginPointerTypeStorage *construct(TypeStorageAllocator &allocator, KeyTy key) -+ { - return new (allocator.allocate()) - PluginPointerTypeStorage(key); - } - -- bool operator==(const KeyTy &key) const { -+ bool operator==(const KeyTy &key) const -+ { - return std::make_tuple(pointee, readOnlyPointee) == key; - } - - Type pointee; - unsigned readOnlyPointee; - }; --} // namespace detail -+} // namespace Detail - } // namespace PluginIR - - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // Plugin TypeBase --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - - PluginTypeID PluginTypeBase::getPluginTypeID () - { -@@ -119,7 +123,7 @@ PluginTypeID PluginTypeBase::getPluginTypeID () - if (auto Ty = dyn_cast()) { - return Ty.getPluginTypeID (); - } -- return PluginTypeID::UndefTyID; -+ return PluginTypeID::UndefTyID; - } - - unsigned PluginTypeBase::getPluginIntOrFloatBitWidth () -@@ -159,9 +163,9 @@ unsigned PluginTypeBase::getTypeSize () - return size; - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // Plugin Integer Type --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - - unsigned PluginIntegerType::getWidth() const - { -@@ -175,10 +179,8 @@ PluginIntegerType::SignednessSemantics PluginIntegerType::getSignedness() const - - PluginTypeID PluginIntegerType::getPluginTypeID() - { -- if (isSigned()) -- { -- switch (getWidth()) -- { -+ if (isSigned()) { -+ switch (getWidth()) { - case 1: - return PluginTypeID::IntegerTy1ID; - case 8: -@@ -193,10 +195,8 @@ PluginTypeID PluginIntegerType::getPluginTypeID() - return PluginTypeID::UndefTyID; - } - } -- if (isUnsigned()) -- { -- switch (getWidth()) -- { -+ if (isUnsigned()) { -+ switch (getWidth()) { - case 1: - return PluginTypeID::UIntegerTy1ID; - case 8: -@@ -214,14 +214,15 @@ PluginTypeID PluginIntegerType::getPluginTypeID() - return PluginTypeID::UndefTyID; - } - --PluginIntegerType PluginIntegerType::get (MLIRContext *context, unsigned width, PluginIntegerType::SignednessSemantics signedness) -+PluginIntegerType PluginIntegerType::get (MLIRContext *context, unsigned width, -+ PluginIntegerType::SignednessSemantics signedness) - { - return Base::get(context, width, signedness); - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // Plugin Float Type --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - - unsigned PluginFloatType::getWidth () const - { -@@ -230,10 +231,12 @@ unsigned PluginFloatType::getWidth () const - - PluginTypeID PluginFloatType::getPluginTypeID() - { -- if (getWidth() == 32) -+ if (getWidth() == 32) { - return PluginTypeID::FloatTyID; -- if (getWidth() == 64) -+ } -+ if (getWidth() == 64) { - return PluginTypeID::DoubleTyID; -+ } - return PluginTypeID::UndefTyID; - } - -@@ -242,36 +245,36 @@ PluginFloatType PluginFloatType::get (MLIRContext *context, unsigned width) - return Base::get(context, width); - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // Plugin Boolean Type --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - - PluginTypeID PluginBooleanType::getPluginTypeID() - { - return PluginTypeID::BooleanTyID; - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // Plugin Void Type --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - - PluginTypeID PluginVoidType::getPluginTypeID() - { - return PluginTypeID::VoidTyID; - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // Plugin Undef Type --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - - PluginTypeID PluginUndefType::getPluginTypeID() - { - return PluginTypeID::UndefTyID; - } - --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - // Plugin Pointer Type --//===----------------------------------------------------------------------===// -+// ===----------------------------------------------------------------------===// - - PluginTypeID PluginPointerType::getPluginTypeID() - { -@@ -291,4 +294,4 @@ unsigned PluginPointerType::isReadOnlyElem() - PluginPointerType PluginPointerType::get (MLIRContext *context, Type pointee, unsigned readOnlyPointee) - { - return Base::get(context, pointee, readOnlyPointee); --} -\ No newline at end of file -+} -diff --git a/lib/PluginAPI/PluginClientAPI.cpp b/lib/PluginAPI/PluginClientAPI.cpp -index 1c4dbf9..5e454d7 100644 ---- a/lib/PluginAPI/PluginClientAPI.cpp -+++ b/lib/PluginAPI/PluginClientAPI.cpp -@@ -19,6 +19,32 @@ - #include "PluginAPI/PluginClientAPI.h" - - namespace PluginAPI { -+ -+string PluginClientAPI::GetDeclSourceFile(uint64_t gccDataAddr) -+{ -+ return gimpleConversion.DeclSourceFile(gccDataAddr); -+} -+ -+string PluginClientAPI::VariableName(int64_t gccDataAddr) -+{ -+ return gimpleConversion.GetVariableName(gccDataAddr); -+} -+ -+string PluginClientAPI::FuncName(int64_t gccDataAddr) -+{ -+ return gimpleConversion.GetFuncName(gccDataAddr); -+} -+ -+int PluginClientAPI::GetDeclSourceLine(uint64_t gccDataAddr) -+{ -+ return gimpleConversion.DeclSourceLine(gccDataAddr); -+} -+ -+int PluginClientAPI::GetDeclSourceColumn(uint64_t gccDataAddr) -+{ -+ return gimpleConversion.DeclSourceColumn(gccDataAddr); -+} -+ - uint64_t PluginClientAPI::CreateBlock(uint64_t funcAddr, uint64_t bbAddr) - { - return gimpleConversion.CreateBlock(funcAddr, bbAddr); -@@ -142,20 +168,17 @@ bool PluginClientAPI::SetLhsInCallOp(uint64_t callId, uint64_t lhsId) - return gimpleConversion.SetGimpleCallLHS(callId, lhsId); - } - --uint32_t PluginClientAPI::AddArgInPhiOp(uint64_t phiId, uint64_t argId, -- uint64_t predId, uint64_t succId) -+uint32_t PluginClientAPI::AddArgInPhiOp(uint64_t phiId, uint64_t argId, uint64_t predId, uint64_t succId) - { - return gimpleConversion.AddPhiArg(phiId, argId, predId, succId); - } - --uint64_t PluginClientAPI::CreateCallOp(uint64_t blockId, uint64_t funcId, -- vector &argIds) -+uint64_t PluginClientAPI::CreateCallOp(uint64_t blockId, uint64_t funcId, vector &argIds) - { - return gimpleConversion.CreateGcallVec(blockId, funcId, argIds); - } - --uint64_t PluginClientAPI::CreateAssignOp(uint64_t blockId, IExprCode iCode, -- vector &argIds) -+uint64_t PluginClientAPI::CreateAssignOp(uint64_t blockId, IExprCode iCode, vector &argIds) - { - return gimpleConversion.CreateGassign(blockId, iCode, argIds); - } -@@ -170,8 +193,7 @@ mlir::Value PluginClientAPI::CreateConstOp(mlir::Attribute attr, mlir::Type type - } - - uint64_t PluginClientAPI::CreateCondOp(uint64_t blockId, IComparisonCode iCode, -- uint64_t LHS, uint64_t RHS, -- uint64_t tbaddr, uint64_t fbaddr) -+ uint64_t LHS, uint64_t RHS, uint64_t tbaddr, uint64_t fbaddr) - { - return gimpleConversion.CreateGcond(blockId, iCode, LHS, RHS, tbaddr, fbaddr); - } -diff --git a/lib/PluginClient/PluginLog.cpp b/lib/PluginClient/PluginLog.cpp -index ad2b985..6cdf7af 100644 ---- a/lib/PluginClient/PluginLog.cpp -+++ b/lib/PluginClient/PluginLog.cpp -@@ -36,6 +36,7 @@ constexpr int LOG_BUF_SIZE = 10240; - constexpr int BASE_DATE = 1900; - static LogPriority g_priority = PRIORITY_WARN; // log打印的级别控制 - static std::mutex g_mutex; // 线程锁 -+static unsigned int g_logFileSize = 10 * 1024 * 1024; - - shared_ptr g_fs; - static void LogWriteInit(const string& data); -@@ -44,16 +45,22 @@ static void (*g_writeToLog)(const string& data) = LogWriteInit; - static void GetLogFileName(string& fileName) - { - time_t nowTime = time(nullptr); -+ if (nowTime == -1) { -+ printf("%s fail\n", __func__); -+ } - struct tm *t = localtime(&nowTime); - char buf[100]; -- sprintf(buf, "/tmp/pin_client%d_%4d%02d%02d_%02d_%02d_%02d.log", getpid(), -+ int ret = sprintf(buf, "/tmp/pin_client%d_%4d%02d%02d_%02d_%02d_%02d.log", getpid(), - t->tm_year + BASE_DATE, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); -+ if (ret < 0) { -+ printf("%s sprintf fail\n", __func__); -+ } - fileName = buf; - } - - static void LogWriteFile(const string& data) - { -- if (g_fs->tellg() > LOG_FILE_SIZE) { -+ if (g_fs->tellg() > g_logFileSize) { - g_fs->close(); - string fileName; - GetLogFileName(fileName); -@@ -75,7 +82,7 @@ static void LogWriteInit(const string& data) - g_writeToLog(data); - } - --void CloseLog(void) -+void CloseLog() - { - if (g_fs) { - if (g_fs->is_open()) { -@@ -84,14 +91,24 @@ void CloseLog(void) - } - } - -+void SetLogFileSize(unsigned int size) -+{ -+ g_logFileSize = size; -+} -+ - static void LogWrite(const char *tag, const char *msg) - { - time_t nowTime = time(nullptr); -+ if (nowTime == -1) { -+ printf("%s fail\n", __func__); -+ } - struct tm *t = localtime(&nowTime); - char buf[30]; -- sprintf(buf, "%4d-%02d-%02d %02d:%02d:%02d ", t->tm_year + BASE_DATE, t->tm_mon + 1, t->tm_mday, -+ int ret = sprintf(buf, "%4d-%02d-%02d %02d:%02d:%02d ", t->tm_year + BASE_DATE, t->tm_mon + 1, t->tm_mday, - t->tm_hour, t->tm_min, t->tm_sec); -- -+ if (ret < 0) { -+ printf("%s sprintf fail\n", __func__); -+ } - string stag = tag; - string smsg = msg; - string data = buf + stag + smsg; -@@ -104,7 +121,10 @@ void LogPrint(LogPriority priority, const char *tag, const char *fmt, ...) - char buf[LOG_BUF_SIZE]; - - va_start(ap, fmt); -- vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); -+ int ret = vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); -+ if (ret < 0) { -+ printf("%s vsnprintf fail\n", __func__); -+ } - va_end(ap); - - if (priority <= g_priority) { -diff --git a/lib/Translate/GimpleToPluginOps.cpp b/lib/Translate/GimpleToPluginOps.cpp -index 3a71ffe..b5974aa 100644 ---- a/lib/Translate/GimpleToPluginOps.cpp -+++ b/lib/Translate/GimpleToPluginOps.cpp -@@ -53,7 +53,7 @@ namespace PluginIR { - using namespace mlir::Plugin; - using namespace mlir; - --namespace detail { -+namespace Detail { - class BlockFromGimpleTranslatorImpl { - public: - std::map blockMaps; -@@ -65,13 +65,13 @@ private: - mlir::MLIRContext &context; - }; - --} // namespace detail -+} // namespace Detail - --GimpleToPluginOps::GimpleToPluginOps (mlir::MLIRContext &context) : -- builder(&context), typeTranslator(context), bbTranslator(new detail::BlockFromGimpleTranslatorImpl(context)) -+GimpleToPluginOps::GimpleToPluginOps(mlir::MLIRContext &context) : builder(&context), -+ typeTranslator(context), bbTranslator(new Detail::BlockFromGimpleTranslatorImpl(context)) - {} - --GimpleToPluginOps::~GimpleToPluginOps () -+GimpleToPluginOps::~GimpleToPluginOps() - {} - - static IComparisonCode TranslateCmpCode(enum tree_code ccode) -@@ -151,7 +151,6 @@ static IExprCode TranslateExprCode(enum tree_code ccode) - case NOP_EXPR: - return IExprCode::Nop; - default: -- // printf("tcc_binary: %d not suppoted!\n", ccode); - break; - } - return IExprCode::UNDEF; -@@ -185,7 +184,6 @@ static enum tree_code TranslateExprCodeToTreeCode(IExprCode ccode) - case IExprCode::Nop: - return NOP_EXPR; - default: -- // printf("tcc_binary: %d not suppoted!\n", ccode); - break; - } - // FIXME. -@@ -220,6 +218,40 @@ static StringRef GimpleCodeToOperationName(enum gimple_code tcode) - return ret; - } - -+string GimpleToPluginOps::DeclSourceFile(uint64_t gccDataAddr) -+{ -+ tree decl = (tree)gccDataAddr; -+ string sourceFile = DECL_SOURCE_FILE(decl); -+ return sourceFile; -+} -+ -+string GimpleToPluginOps::GetVariableName(uint64_t gccDataAddr) -+{ -+ tree decl = (tree)gccDataAddr; -+ string pointer = DECL_NAME(decl) != NULL_TREE ? IDENTIFIER_POINTER(DECL_NAME(decl)) : ""; -+ return pointer; -+} -+ -+string GimpleToPluginOps::GetFuncName(uint64_t gccDataAddr) -+{ -+ string funcName = function_name((function *)gccDataAddr); -+ return funcName; -+} -+ -+int GimpleToPluginOps::DeclSourceLine(uint64_t gccDataAddr) -+{ -+ tree decl = (tree)gccDataAddr; -+ int line = DECL_SOURCE_LINE(decl); -+ return line; -+} -+ -+int GimpleToPluginOps::DeclSourceColumn(uint64_t gccDataAddr) -+{ -+ tree decl = (tree)gccDataAddr; -+ int column = DECL_SOURCE_COLUMN(decl); -+ return column; -+} -+ - uint64_t GimpleToPluginOps::CreateBlock(uint64_t funcAddr, uint64_t bbAddr) - { - basic_block address = reinterpret_cast(bbAddr); -@@ -484,7 +516,7 @@ void GimpleToPluginOps::RedirectFallthroughTarget(uint64_t src, uint64_t dest) - { - basic_block srcbb = reinterpret_cast(reinterpret_cast(src)); - basic_block destbb = reinterpret_cast(reinterpret_cast(dest)); -- assert(single_succ_p (srcbb)); -+ assert(single_succ_p(srcbb)); - redirect_edge_and_branch (single_succ_edge(srcbb), destbb); - } - -@@ -537,7 +569,7 @@ Operation *GimpleToPluginOps::BuildOperation(uint64_t id) - break; - } - case GIMPLE_COND: { -- assert(EDGE_COUNT (stmt->bb->succs) == 2); -+ assert(EDGE_COUNT(stmt->bb->succs) == 2); - Block* trueBlock = bbTranslator->blockMaps[EDGE_SUCC(stmt->bb, 0)->dest]; - Block* falseBlock = bbTranslator->blockMaps[EDGE_SUCC(stmt->bb, 1)->dest]; - CondOp condOp = BuildCondOp(id, (uint64_t)stmt->bb, -@@ -644,15 +676,13 @@ uint64_t GimpleToPluginOps::CreateGcallVec(uint64_t blockId, uint64_t funcId, - gcall *ret = gimple_build_call_vec (fn, vargs); - basic_block bb = reinterpret_cast(blockId); - if (bb != nullptr) { -- gimple_stmt_iterator si; -- si = gsi_last_bb (bb); -+ gimple_stmt_iterator si = gsi_last_bb (bb); - gsi_insert_after (&si, ret, GSI_NEW_STMT); - } - return reinterpret_cast(reinterpret_cast(ret)); - } - --uint32_t GimpleToPluginOps::AddPhiArg(uint64_t phiId, uint64_t argId, -- uint64_t predId, uint64_t succId) -+uint32_t GimpleToPluginOps::AddPhiArg(uint64_t phiId, uint64_t argId, uint64_t predId, uint64_t succId) - { - gphi *phi = reinterpret_cast(phiId); - tree arg = reinterpret_cast(argId); -@@ -681,31 +711,25 @@ uint64_t GimpleToPluginOps::CreateGassign(uint64_t blockId, IExprCode iCode, - if (iCode == IExprCode::UNDEF) { - ret = gimple_build_assign(vargs[0], vargs[1]); - } else { -- ret = gimple_build_assign(vargs[0], -- TranslateExprCodeToTreeCode(iCode), -- vargs[1]); -+ ret = gimple_build_assign(vargs[0], TranslateExprCodeToTreeCode(iCode), vargs[1]); - } - } else if (vargs.size() == 3) { -- ret = gimple_build_assign(vargs[0], TranslateExprCodeToTreeCode(iCode), -- vargs[1], vargs[2]); -+ ret = gimple_build_assign(vargs[0], TranslateExprCodeToTreeCode(iCode), vargs[1], vargs[2]); - } else if (vargs.size() == 4) { -- ret = gimple_build_assign(vargs[0], TranslateExprCodeToTreeCode(iCode), -- vargs[1], vargs[2], vargs[3]); -+ ret = gimple_build_assign(vargs[0], TranslateExprCodeToTreeCode(iCode), vargs[1], vargs[2], vargs[3]); - } else { - printf("ERROR size: %ld.\n", vargs.size()); - } - basic_block bb = reinterpret_cast(blockId); - if (bb != nullptr) { -- gimple_stmt_iterator si; -- si = gsi_last_bb (bb); -+ gimple_stmt_iterator si = gsi_last_bb (bb); - gsi_insert_after (&si, ret, GSI_NEW_STMT); - } - return reinterpret_cast(reinterpret_cast(ret)); - } - - uint64_t GimpleToPluginOps::CreateGcond(uint64_t blockId, IComparisonCode iCode, -- uint64_t lhsId, uint64_t rhsId, -- uint64_t tbaddr, uint64_t fbaddr) -+ uint64_t lhsId, uint64_t rhsId, uint64_t tbaddr, uint64_t fbaddr) - { - tree lhs = reinterpret_cast(lhsId); - tree rhs = reinterpret_cast(rhsId); -@@ -713,14 +737,13 @@ uint64_t GimpleToPluginOps::CreateGcond(uint64_t blockId, IComparisonCode iCode, - lhs, rhs, NULL_TREE, NULL_TREE); - basic_block bb = reinterpret_cast(blockId); - if (bb != nullptr) { -- gimple_stmt_iterator si; -- si = gsi_last_bb (bb); -+ gimple_stmt_iterator si = gsi_last_bb (bb); - gsi_insert_after (&si, ret, GSI_NEW_STMT); - } - basic_block tb = reinterpret_cast(tbaddr); - basic_block fb = reinterpret_cast(fbaddr); -- assert(make_edge (bb, tb, EDGE_TRUE_VALUE)); -- assert(make_edge (bb, fb, EDGE_FALSE_VALUE)); -+ assert(make_edge(bb, tb, EDGE_TRUE_VALUE)); -+ assert(make_edge(bb, fb, EDGE_FALSE_VALUE)); - return reinterpret_cast(reinterpret_cast(ret)); - } - -@@ -728,12 +751,11 @@ void GimpleToPluginOps::CreateFallthroughOp(uint64_t addr, uint64_t destaddr) - { - basic_block src = reinterpret_cast(addr); - basic_block dest = reinterpret_cast(destaddr); -- assert(make_single_succ_edge (src, dest, EDGE_FALLTHRU)); -+ assert(make_single_succ_edge(src, dest, EDGE_FALLTHRU)); - } - - CondOp GimpleToPluginOps::BuildCondOp(uint64_t gcondId, uint64_t address, -- Block* b1, Block* b2, uint64_t tbaddr, -- uint64_t fbaddr) -+ Block* b1, Block* b2, uint64_t tbaddr, uint64_t fbaddr) - { - gcond *stmt = reinterpret_cast(gcondId); - tree lhsPtr = gimple_cond_lhs(stmt); -@@ -812,16 +834,9 @@ Value GimpleToPluginOps::TreeToValue(uint64_t treeId) - mlir::Value opValue; - switch (TREE_CODE(t)) { - case INTEGER_CST : { -- mlir::Attribute initAttr; -- if (tree_fits_shwi_p(t)) { -- signed HOST_WIDE_INT sinit = tree_to_shwi(t); -- initAttr = builder.getI64IntegerAttr(sinit); -- } else if (tree_fits_uhwi_p(t)) { -- unsigned HOST_WIDE_INT uinit = tree_to_uhwi(t); -- initAttr = builder.getI64IntegerAttr(uinit); -- } else { -- abort(); -- } -+ unsigned HOST_WIDE_INT init = tree_to_uhwi(t); -+ // FIXME : AnyAttr! -+ mlir::Attribute initAttr = builder.getI64IntegerAttr(init); - opValue = builder.create( - builder.getUnknownLoc(), treeId, IDefineCode::IntCST, - readOnly, initAttr, rPluginType); -@@ -863,11 +878,9 @@ Value GimpleToPluginOps::TreeToValue(uint64_t treeId) - void GimpleToPluginOps::DebugValue(uint64_t valId) - { - tree t = reinterpret_cast(valId); -- // debug_tree (t); - } - --mlir::Value GimpleToPluginOps::BuildMemRef(PluginIR::PluginTypeBase type, -- uint64_t baseId, uint64_t offsetId) -+mlir::Value GimpleToPluginOps::BuildMemRef(PluginIR::PluginTypeBase type, uint64_t baseId, uint64_t offsetId) - { - tree refType = (tree)pluginTypeTranslator.translateType(type); - tree base = (tree)baseId; -@@ -883,7 +896,7 @@ bool GimpleToPluginOps::ProcessGimpleStmt(intptr_t bbPtr, Region& rg) - for (gphi_iterator si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si)) { - gphi *p = si.phi (); - uint64_t id = reinterpret_cast(reinterpret_cast(p)); -- BuildPhiOp(id); // FIXME: Check result. -+ BuildPhiOp(id); // FIXME: Check result. - } - - for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si)) { -@@ -892,7 +905,7 @@ bool GimpleToPluginOps::ProcessGimpleStmt(intptr_t bbPtr, Region& rg) - if (BuildOperation(id) == nullptr) { - printf("ERROR: BuildOperation!"); - } -- if(gimple_code(stmt) == GIMPLE_COND) { -+ if (gimple_code(stmt) == GIMPLE_COND) { - putTerminator = true; - } - } -@@ -907,7 +920,6 @@ bool GimpleToPluginOps::ProcessGimpleStmt(intptr_t bbPtr, Region& rg) - // Process other condition, such as return - builder.create(builder.getUnknownLoc(), (uint64_t)bb); - } else { -- // Should unreachable; - assert(false); - } - } -diff --git a/lib/Translate/TypeTranslation.cpp b/lib/Translate/TypeTranslation.cpp -index 66521a9..7c7cff4 100644 ---- a/lib/Translate/TypeTranslation.cpp -+++ b/lib/Translate/TypeTranslation.cpp -@@ -49,7 +49,7 @@ - using namespace mlir; - - namespace PluginIR { --namespace detail { -+namespace Detail { - /* Support for translating Plugin IR types to MLIR Plugin dialect types. */ - class TypeFromPluginIRTranslatorImpl { - public: -@@ -82,7 +82,8 @@ private: - PluginTypeBase translatePrimitiveType (tree type) - { - if (TREE_CODE(type) == INTEGER_TYPE) -- return PluginIntegerType::get(&context, getBitWidth(type), isUnsigned(type) ? PluginIntegerType::Unsigned : PluginIntegerType::Signed); -+ return PluginIntegerType::get(&context, getBitWidth(type), -+ isUnsigned(type) ? PluginIntegerType::Unsigned : PluginIntegerType::Signed); - if (TREE_CODE(type) == REAL_TYPE) - return PluginFloatType::get(&context, getBitWidth(type)); - if (TREE_CODE(type) == BOOLEAN_TYPE) -@@ -90,7 +91,8 @@ private: - if (TREE_CODE(type) == VOID_TYPE) - return PluginVoidType::get(&context); - if (TREE_CODE(type) == POINTER_TYPE) -- return PluginPointerType::get(&context, translatePrimitiveType(TREE_TYPE(type)), TYPE_READONLY(TREE_TYPE(type)) ? 1 : 0); -+ return PluginPointerType::get(&context, translatePrimitiveType(TREE_TYPE(type)), -+ TYPE_READONLY(TREE_TYPE(type)) ? 1 : 0); - return PluginUndefType::get(&context); - } - -@@ -111,19 +113,20 @@ public: - } - - private: -- unsigned getBitWidth (PluginIntegerType type) -+ unsigned getBitWidth(PluginIntegerType type) - { - return type.getWidth(); - } - -- bool isUnsigned (PluginIntegerType type) -+ bool isUnsigned(PluginIntegerType type) - { -- if(type.isUnsigned()) -+ if (type.isUnsigned()) { - return true; -+ } - return false; - } - -- tree translatePrimitiveType (PluginTypeBase type) -+ tree translatePrimitiveType(PluginTypeBase type) - { - if (auto Ty = type.dyn_cast()) { - if (isUnsigned(Ty)) { -@@ -178,14 +181,13 @@ private: - } - return NULL; - } -- - }; - --} // namespace detail -+} // namespace Detail - } // namespace PluginIR - - PluginIR::TypeFromPluginIRTranslator::TypeFromPluginIRTranslator(mlir::MLIRContext &context) -- : impl(new detail::TypeFromPluginIRTranslatorImpl(context)) {} -+ : impl(new Detail::TypeFromPluginIRTranslatorImpl(context)) {} - - PluginIR::TypeFromPluginIRTranslator::~TypeFromPluginIRTranslator() {} - -@@ -206,7 +208,7 @@ uint64_t PluginIR::TypeFromPluginIRTranslator::getBitWidth(PluginTypeBase type) - - - PluginIR::TypeToPluginIRTranslator::TypeToPluginIRTranslator() -- : impl(new detail::TypeToPluginIRTranslatorImpl()) {} -+ : impl(new Detail::TypeToPluginIRTranslatorImpl()) {} - - PluginIR::TypeToPluginIRTranslator::~TypeToPluginIRTranslator() {} - -diff --git a/lib/IRTrans/IRTransPlugin.cpp b/lib/gccPlugin/gccPlugin.cpp -old mode 100644 -new mode 100755 -similarity index 52% -rename from lib/IRTrans/IRTransPlugin.cpp -rename to lib/gccPlugin/gccPlugin.cpp -index 459cef0..103d709 ---- a/lib/IRTrans/IRTransPlugin.cpp -+++ b/lib/gccPlugin/gccPlugin.cpp -@@ -17,78 +17,81 @@ - Author: Mingchuan Wu and Yancheng Li - Create: 2022-08-18 - Description: -- This file contains the declaration of the PluginAPI class. -+ This file contains the functions of gcc plugin callback and init. -+ 主要完成功能:实现RegisterPluginEvent和RegisterPassManagerSetup功能,完成gcc -+ 版本号检查,当触发注册点对应事件时,回调server注册的用户函数 -+ plugin_init为整个程序入口函数 - */ - --#include --#include --#include - #include --#include - #include "PluginClient/PluginClient.h" - #include "plugin-version.h" --#include "IRTrans/IRTransPlugin.h" --#include "context.h" - #include "tree-pass.h" - #include "tree.h" --#include "tree-cfg.h" -+#include "context.h" -+#include "gccPlugin/gccPlugin.h" - - using namespace PinClient; - --using std::vector; - int plugin_is_GPL_compatible; - static pid_t g_serverPid; - --/* gcc插件end事件回调函数 */ --static void GccEnd(void *gccData, void *userData) -+/* gcc插件end事件回调函数,停止插件服务端,等待子进程结束,删除端口号 */ -+void GccEnd(void *gccData, void *userData) - { - int status = 0; -- std::shared_ptr client = PluginClient::GetInstance(); -- if (client == nullptr) { -+ PluginClient *client = PluginClient::GetInstance(); -+ if (!client->GetStartFlag()) { - return; - } -+ - LOGI("gcc optimize has been done! now close server...\n"); - client->ReceiveSendMsg("stop", ""); - if (client->GetUserFuncState() != STATE_TIMEOUT) { - waitpid(g_serverPid, &status, 0); - } else { -- client->DeletePortFromLockFile(client->GetGrpcPort()); -+ client->DeleteGrpcPort(); - } -- - LOGI("client pid:%d quit\n", getpid()); - } - -+static void WaitIRTrans(void *gccData, PluginClient *client) -+{ -+ while (1) { -+ UserFuncStateEnum state = client->GetUserFuncState(); -+ /* server获取到client对应函数的执行结果后,向client回复已执行完,跳出循环执行下一个函数 */ -+ if (state == STATE_END) { -+ client->SetUserFuncState(STATE_WAIT_BEGIN); -+ break; -+ } else if (state == STATE_TIMEOUT) { -+ break; -+ } else if (state == STATE_BEGIN) { -+ string funcName = client->GetPluginAPIName(); -+ string param = client->GetPluginAPIParam(); -+ if (funcName != "") { -+ client->SetUserFuncState(STATE_WAIT_IR); -+ client->GetIRTransResult(gccData, funcName, param); -+ } -+ } -+ } -+} -+ - /* gcc插件回调函数,当注册的plugin_event触发时,进入此函数 */ --static void GccEventCallback(void *gccData, void *userData) -+void GccEventCallback(void *gccData, void *userData) - { -- std::shared_ptr client = PluginClient::GetInstance(); -+ PluginClient *client = PluginClient::GetInstance(); - InjectPoint *inject = (InjectPoint *)userData; - vector userFuncs = client->GetFuncNameByInject(*inject); - string key = "injectPoint"; - string value; -+ - for (auto &userFunc : userFuncs) { - if (client->GetUserFuncState() == STATE_TIMEOUT) { - break; - } - value = std::to_string(*inject) + ":" + userFunc; - client->ReceiveSendMsg(key, value); -- while (1) { -- UserFuncStateEnum state = client->GetUserFuncState(); -- /* server获取到client对应函数的执行结果后,向client回复已执行完,跳出循环执行下一个函数 */ -- if (state == STATE_END) { -- client->SetUserFuncState(STATE_WAIT_BEGIN); -- break; -- } else if (state == STATE_TIMEOUT) { -- break; -- } else if (state == STATE_BEGIN) { -- string funcName = client->GetPluginAPIName(); -- string param = client->GetPluginAPIParam(); -- if (funcName != "") { -- client->SetUserFuncState(STATE_WAIT_IR); -- client->IRTransBegin(funcName, param); -- } -- } -- } -+ WaitIRTrans(gccData, client); - } - LOGI("%s end!\n", __func__); - } -@@ -107,6 +110,7 @@ static InjectPoint g_event[] = { - HANDLE_AFTER_ALL_PASS, - HANDLE_COMPILE_END, - HANDLE_MANAGER_SETUP, -+ HANDLE_INCLUDE_FILE, - HANDLE_MAX - }; - -@@ -121,84 +125,67 @@ int RegisterPluginEvent(InjectPoint inject, const string& pluginName) - return 0; - } - --void ManagerSetupCallback(void) -+static void ManagerSetupCallback(unsigned int index, function *fun) - { - string key = "injectPoint"; - InjectPoint inject = HANDLE_MANAGER_SETUP; -- std::shared_ptr client = PluginClient::GetInstance(); -+ PluginClient *client = PluginClient::GetInstance(); - vector userFuncs = client->GetFuncNameByInject(inject); -- for (auto &userFunc : userFuncs) { -- if (client->GetUserFuncState() == STATE_TIMEOUT) { -- break; -- } -- string value = std::to_string(inject) + ":" + userFunc; -+ if (index < userFuncs.size()) { -+ string name = userFuncs[index].substr(0, userFuncs[index].find_first_of(",")); -+ string value = std::to_string(inject) + ":" + name + ",params:" + std::to_string((uintptr_t)fun); - client->ReceiveSendMsg(key, value); -- while (1) { -- UserFuncStateEnum state = client->GetUserFuncState(); -- /* server获取到client对应函数的执行结果后,向client回复已执行完,跳出循环执行下一个函数 */ -- if (state == STATE_END) { -- client->SetUserFuncState(STATE_WAIT_BEGIN); -- break; -- } else if (state == STATE_TIMEOUT) { -- break; -- } else if (state == STATE_BEGIN) { -- string funcName = client->GetPluginAPIName(); -- string param = client->GetPluginAPIParam(); -- if (funcName != "") { -- client->SetUserFuncState(STATE_WAIT_IR); -- client->IRTransBegin(funcName, param); -- } -- } -- } -+ WaitIRTrans(nullptr, client); - } - } - --struct RltPass: rtl_opt_pass { -+struct RtlPass: rtl_opt_pass { - public: -- RltPass(pass_data passData): rtl_opt_pass(passData, g) -+ RtlPass(pass_data passData, unsigned int indx): rtl_opt_pass(passData, g), index(indx) - { - } -- unsigned int execute(function *fun) override -+ /* unsigned int execute(function *fun) override - { -- ManagerSetupCallback(); -+ ManagerSetupCallback(index, fun); - return 0; -- } -- RltPass* clone() override -- { -- return this; -- } -+ } */ -+ -+private: -+ unsigned int index; - }; - - struct SimpleIPAPass: simple_ipa_opt_pass { - public: -- SimpleIPAPass(pass_data passData): simple_ipa_opt_pass(passData, g) -+ SimpleIPAPass(pass_data passData, unsigned int indx): simple_ipa_opt_pass(passData, g), index(indx) - { - } -- unsigned int execute(function *fun) override -+ /* unsigned int execute(function *fun) override - { -- ManagerSetupCallback(); -+ ManagerSetupCallback(index, fun); - return 0; -- } -- SimpleIPAPass* clone() override -- { -- return this; -- } -+ } */ -+ -+private: -+ unsigned int index; - }; - - struct GimplePass: gimple_opt_pass { - public: -- GimplePass(pass_data passData): gimple_opt_pass(passData, g) -+ GimplePass(pass_data passData, unsigned int indx): gimple_opt_pass(passData, g), index(indx) - { - } - unsigned int execute(function *fun) override - { -- ManagerSetupCallback(); -+ ManagerSetupCallback(index, fun); - return 0; - } - GimplePass* clone() override - { - return this; - } -+ -+private: -+ unsigned int index; - }; - - static std::map g_refPassName { -@@ -208,16 +195,13 @@ static std::map g_refPassName { - {PASS_LOOP, "loop"}, - }; - --int RegisterPassManagerSetup(InjectPoint inject, const ManagerSetupData& setupData, const string& pluginName) -+void RegisterPassManagerSetup(unsigned int index, const ManagerSetupData& setupData, const string& pluginName) - { -- if (inject != HANDLE_MANAGER_SETUP) { -- return -1; -- } -- - struct register_pass_info passInfo; -+ string passDataName = "managerSetupPass_" + g_refPassName[setupData.refPassName]; - pass_data passData = { - .type = GIMPLE_PASS, -- .name = "managerSetupPass", -+ .name = passDataName.c_str(), - .optinfo_flags = OPTGROUP_NONE, - .tv_id = TV_NONE, - .properties_required = 0, -@@ -233,27 +217,28 @@ int RegisterPassManagerSetup(InjectPoint inject, const ManagerSetupData& setupDa - switch (setupData.refPassName) { - case PASS_CFG: - passData.type = GIMPLE_PASS; -- passInfo.pass = new GimplePass(passData); -+ passInfo.pass = new GimplePass(passData, index); - break; - case PASS_PHIOPT: - passData.type = GIMPLE_PASS; -- passInfo.pass = new GimplePass(passData); -+ passInfo.pass = new GimplePass(passData, index); - break; - case PASS_SSA: - passData.type = GIMPLE_PASS; -- passInfo.pass = new GimplePass(passData); -+ passInfo.pass = new GimplePass(passData, index); - break; - case PASS_LOOP: - passData.type = GIMPLE_PASS; -- passInfo.pass = new GimplePass(passData); -+ passInfo.pass = new GimplePass(passData, index); - break; - default: -- passInfo.pass = new GimplePass(passData); -+ passInfo.pass = new GimplePass(passData, index); - break; - } - -- register_callback(pluginName.c_str(), PLUGIN_PASS_MANAGER_SETUP, NULL, &passInfo); -- return 0; -+ if (pluginName != "") { -+ register_callback(pluginName.c_str(), PLUGIN_PASS_MANAGER_SETUP, NULL, &passInfo); -+ } - } - - static bool PluginVersionCheck(struct plugin_gcc_version *gccVersion, struct plugin_gcc_version *pluginVersion) -@@ -274,46 +259,14 @@ static bool PluginVersionCheck(struct plugin_gcc_version *gccVersion, struct plu - - int plugin_init(struct plugin_name_args *pluginInfo, struct plugin_gcc_version *version) - { -+ if (!PluginVersionCheck(version, &gcc_version)) { -+ LOGE("incompatible gcc/plugin versions\n"); -+ return 1; -+ } - string pluginName = pluginInfo->base_name; - register_callback(pluginName.c_str(), PLUGIN_FINISH, &GccEnd, NULL); - -- int timeout = 200; // 默认超时时间200ms -- string shaPath; -- string serverPath = ""; -- string arg = ""; -- LogPriority logLevel = PRIORITY_WARN; -- PluginClient::GetArg(pluginInfo, serverPath, arg, logLevel); -- if (PluginClient::GetInitInfo(serverPath, shaPath, timeout) != 0) { -- LOGD("read default info from pin-gcc-client.json fail! use the default timeout=%dms\n", timeout); -- } -- if (serverPath == "") { -- LOGE("server path is NULL!\n"); -- return 0; -- } -- -- if (PluginClient::CheckSHA256(shaPath) != 0) { -- LOGE("sha256 check sha256 file:%s fail!\n", shaPath.c_str()); -- return 0; -- } else { -- LOGD("sha256 check success!\n"); -- } -- -- string port; -- int status; -- if (ServerStart(timeout, serverPath, g_serverPid, port, logLevel) != 0) { -- return 0; -- } -- ClientStart(timeout, arg, pluginName, port); -- std::shared_ptr client = PluginClient::GetInstance(); -- while (1) { -- if ((client->GetInjectFlag()) || (client->GetUserFuncState() == STATE_TIMEOUT)) { -- break; -- } -- if (g_serverPid == waitpid(-1, &status, WNOHANG)) { -- PluginClient::DeletePortFromLockFile((unsigned short)atoi(port.c_str())); -- break; -- } -- } -+ PluginClient::GetInstance()->Init(pluginInfo, pluginName, g_serverPid); - return 0; - } - --- -2.27.0.windows.1 - diff --git a/0005-Pin-gcc-client-Add-DebugOp.patch b/0005-Pin-gcc-client-Add-DebugOp.patch deleted file mode 100644 index 8f5a668..0000000 --- a/0005-Pin-gcc-client-Add-DebugOp.patch +++ /dev/null @@ -1,106 +0,0 @@ -From c0da500cd20a295521d650cfcdb277b66d18bba5 Mon Sep 17 00:00:00 2001 -From: benniaobufeijiushiji -Date: Sun, 19 Feb 2023 11:44:30 +0800 -Subject: [PATCH 5/9] [Pin-gcc-client] Add DebugOp - - -diff --git a/include/Dialect/PluginOps.td b/include/Dialect/PluginOps.td -index c48d002..bac84be 100644 ---- a/include/Dialect/PluginOps.td -+++ b/include/Dialect/PluginOps.td -@@ -235,6 +235,16 @@ def BaseOp : Plugin_Op<"statement_base", [NoSideEffect]> { - ]; - } - -+def DebugOp : Plugin_Op<"debug", [NoSideEffect]> { -+ let summary = "DebugOp."; -+ let description = [{TODO}]; -+ let arguments = (ins UI64Attr:$id); -+ let results = (outs AnyType); -+ let builders = [ -+ OpBuilderDAG<(ins "uint64_t":$id)> -+ ]; -+} -+ - // Terminators - // Opaque builder used for terminator operations that contain successors. - -diff --git a/include/PluginClient/PluginJson.h b/include/PluginClient/PluginJson.h -index 91bd925..a0aac8a 100755 ---- a/include/PluginClient/PluginJson.h -+++ b/include/PluginClient/PluginJson.h -@@ -53,6 +53,7 @@ public: - Json::Value PhiOpJsonSerialize(mlir::Plugin::PhiOp& data); - Json::Value AssignOpJsonSerialize(mlir::Plugin::AssignOp& data); - Json::Value BaseOpJsonSerialize(mlir::Plugin::BaseOp data); -+ Json::Value DebugOpJsonSerialize(mlir::Plugin::DebugOp data); - Json::Value FallThroughOpJsonSerialize(mlir::Plugin::FallThroughOp data, uint64_t&); - Json::Value RetOpJsonSerialize(mlir::Plugin::RetOp data, uint64_t&); - Json::Value ValueJsonSerialize(mlir::Value value); -diff --git a/lib/Dialect/PluginOps.cpp b/lib/Dialect/PluginOps.cpp -index 052ebfd..da5f3f3 100644 ---- a/lib/Dialect/PluginOps.cpp -+++ b/lib/Dialect/PluginOps.cpp -@@ -243,6 +243,15 @@ void BaseOp::build(OpBuilder &builder, OperationState &state, uint64_t id, Strin - state.addAttribute("opCode", builder.getStringAttr(opCode)); - } - -+//===----------------------------------------------------------------------===// -+// DebugOp -+ -+void DebugOp::build(OpBuilder &builder, OperationState &state, -+ uint64_t id) -+{ -+ state.addAttribute("id", builder.getI64IntegerAttr(id)); -+} -+ - // ===----------------------------------------------------------------------===// - // FallThroughOp - -diff --git a/lib/PluginClient/PluginJson.cpp b/lib/PluginClient/PluginJson.cpp -index 22cd489..ccadf10 100755 ---- a/lib/PluginClient/PluginJson.cpp -+++ b/lib/PluginClient/PluginJson.cpp -@@ -182,6 +182,8 @@ Json::Value PluginJson::OperationJsonSerialize(mlir::Operation *operation, uint6 - root = RetOpJsonSerialize(op, bbId); - } else if (BaseOp op = llvm::dyn_cast(operation)) { - root = BaseOpJsonSerialize(op); -+ } else if (DebugOp op = llvm::dyn_cast(operation)) { -+ root = DebugOpJsonSerialize(op); - } - root["OperationName"] = operation->getName().getStringRef().str(); - return root; -@@ -203,6 +205,13 @@ Json::Value PluginJson::RetOpJsonSerialize(RetOp data, uint64_t &bbId) - return root; - } - -+Json::Value PluginJson::DebugOpJsonSerialize(DebugOp data) -+{ -+ Json::Value root; -+ root["id"] = std::to_string(data.idAttr().getInt()); -+ return root; -+} -+ - Json::Value PluginJson::FallThroughOpJsonSerialize(FallThroughOp data, uint64_t &bbId) - { - Json::Value root; -diff --git a/lib/Translate/GimpleToPluginOps.cpp b/lib/Translate/GimpleToPluginOps.cpp -index b5974aa..2f46f6f 100644 ---- a/lib/Translate/GimpleToPluginOps.cpp -+++ b/lib/Translate/GimpleToPluginOps.cpp -@@ -579,6 +579,12 @@ Operation *GimpleToPluginOps::BuildOperation(uint64_t id) - ret = condOp.getOperation(); - break; - } -+ case GIMPLE_DEBUG: { -+ DebugOp debugOp = builder.create( -+ builder.getUnknownLoc(), id); -+ ret = debugOp.getOperation(); -+ break; -+ } - default: { - BaseOp baseOp = builder.create( - builder.getUnknownLoc(), id, BaseOp::getOperationName()); --- -2.27.0.windows.1 - diff --git a/0006-Pin-gcc-client-Bugfix-for-ConstOp.patch b/0006-Pin-gcc-client-Bugfix-for-ConstOp.patch deleted file mode 100644 index c83a8f8..0000000 --- a/0006-Pin-gcc-client-Bugfix-for-ConstOp.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 3750fd8c57d37a103b698eb7e479a98ccc68bd02 Mon Sep 17 00:00:00 2001 -From: benniaobufeijiushiji -Date: Sun, 19 Feb 2023 11:48:16 +0800 -Subject: [PATCH 6/9] [Pin-gcc-client] Bugfix for ConstOp Support both signed - const int and unsigned const int - - -diff --git a/lib/Translate/GimpleToPluginOps.cpp b/lib/Translate/GimpleToPluginOps.cpp -index 2f46f6f..5ecab75 100644 ---- a/lib/Translate/GimpleToPluginOps.cpp -+++ b/lib/Translate/GimpleToPluginOps.cpp -@@ -840,13 +840,16 @@ Value GimpleToPluginOps::TreeToValue(uint64_t treeId) - mlir::Value opValue; - switch (TREE_CODE(t)) { - case INTEGER_CST : { -- unsigned HOST_WIDE_INT init = tree_to_uhwi(t); -- // FIXME : AnyAttr! -- mlir::Attribute initAttr = builder.getI64IntegerAttr(init); -- opValue = builder.create( -- builder.getUnknownLoc(), treeId, IDefineCode::IntCST, -- readOnly, initAttr, rPluginType); -- break; -+ mlir::Attribute initAttr; -+ if (tree_fits_shwi_p(t)) { -+ signed HOST_WIDE_INT sinit = tree_to_shwi(t); -+ initAttr = builder.getI64IntegerAttr(sinit); -+ } else if (tree_fits_uhwi_p(t)) { -+ unsigned HOST_WIDE_INT uinit = tree_to_uhwi(t); -+ initAttr = builder.getI64IntegerAttr(uinit); -+ } else { -+ abort(); -+ } - } - case MEM_REF : { - tree operand0 = TREE_OPERAND(t, 0); --- -2.27.0.windows.1 - diff --git a/0007-Pin-gcc-client-Add-API-for-LTO-judgement.patch b/0007-Pin-gcc-client-Add-API-for-LTO-judgement.patch deleted file mode 100644 index 08e5c38..0000000 --- a/0007-Pin-gcc-client-Add-API-for-LTO-judgement.patch +++ /dev/null @@ -1,131 +0,0 @@ -From 9dc1cc4cb0eeb114ac9b5daea3005f2caa1ccf5b Mon Sep 17 00:00:00 2001 -From: benniaobufeijiushiji -Date: Sun, 19 Feb 2023 14:41:49 +0800 -Subject: [PATCH 7/9] [Pin-gcc-client] Add API for LTO judgement - - -diff --git a/include/PluginAPI/BasicPluginOpsAPI.h b/include/PluginAPI/BasicPluginOpsAPI.h -index 8c47c58..ca74588 100644 ---- a/include/PluginAPI/BasicPluginOpsAPI.h -+++ b/include/PluginAPI/BasicPluginOpsAPI.h -@@ -95,6 +95,9 @@ public: - virtual mlir::Value BuildMemRef(PluginTypeBase, uint64_t, uint64_t) = 0; - virtual void RedirectFallthroughTarget(uint64_t, uint64_t) = 0; - virtual void RemoveEdge(uint64_t, uint64_t) = 0; -+ -+ virtual bool IsLtoOptimize() = 0; -+ virtual bool IsWholeProgram() = 0; - }; // class BasicPluginOpsAPI - } // namespace PluginAPI - -diff --git a/include/PluginAPI/PluginClientAPI.h b/include/PluginAPI/PluginClientAPI.h -index 07d6e52..727d329 100644 ---- a/include/PluginAPI/PluginClientAPI.h -+++ b/include/PluginAPI/PluginClientAPI.h -@@ -95,6 +95,9 @@ public: - - void RedirectFallthroughTarget(uint64_t, uint64_t) override; - void RemoveEdge(uint64_t, uint64_t) override; -+ -+ bool IsLtoOptimize() override; -+ bool IsWholeProgram() override; - private: - PluginIR::GimpleToPluginOps gimpleConversion; - }; // class PluginClientAPI -diff --git a/include/Translate/GimpleToPluginOps.h b/include/Translate/GimpleToPluginOps.h -index 5f8bdf0..2ecf5ac 100644 ---- a/include/Translate/GimpleToPluginOps.h -+++ b/include/Translate/GimpleToPluginOps.h -@@ -106,6 +106,10 @@ public: - - void RedirectFallthroughTarget(uint64_t, uint64_t); - void RemoveEdge(uint64_t, uint64_t); -+ -+ bool IsLtoOptimize(); -+ bool IsWholeProgram(); -+ - private: - GimpleToPluginOps () = delete; - mlir::OpBuilder builder; -diff --git a/lib/PluginAPI/PluginClientAPI.cpp b/lib/PluginAPI/PluginClientAPI.cpp -index 5e454d7..362ede1 100644 ---- a/lib/PluginAPI/PluginClientAPI.cpp -+++ b/lib/PluginAPI/PluginClientAPI.cpp -@@ -280,4 +280,14 @@ void PluginClientAPI::RemoveEdge(uint64_t src, uint64_t dest) - return gimpleConversion.RemoveEdge(src, dest); - } - -+bool PluginClientAPI::IsLtoOptimize() -+{ -+ return gimpleConversion.IsLtoOptimize(); -+} -+ -+bool PluginClientAPI::IsWholeProgram() -+{ -+ return gimpleConversion.IsWholeProgram(); -+} -+ - } // namespace PluginAPI -\ No newline at end of file -diff --git a/lib/PluginClient/PluginClient.cpp b/lib/PluginClient/PluginClient.cpp -index 18877c2..81b59c0 100644 ---- a/lib/PluginClient/PluginClient.cpp -+++ b/lib/PluginClient/PluginClient.cpp -@@ -820,6 +820,24 @@ void DebugValueResult(PluginClient *client, Json::Value& root, string& result) - client->ReceiveSendMsg("ValueResult", result); - } - -+void IsLtoOptimizeResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ bool lto = clientAPI.IsLtoOptimize(); -+ client->ReceiveSendMsg("BoolResult", std::to_string(lto)); -+} -+ -+void IsWholeProgramResult(PluginClient *client, Json::Value& root, string& result) -+{ -+ mlir::MLIRContext context; -+ context.getOrLoadDialect(); -+ PluginAPI::PluginClientAPI clientAPI(context); -+ bool wholePR = clientAPI.IsWholeProgram(); -+ client->ReceiveSendMsg("BoolResult", std::to_string(wholePR)); -+} -+ - typedef std::function GetResultFunc; - std::map g_getResultFunc = { - {"GetAllFunc", GetAllFuncResult}, -@@ -868,6 +886,8 @@ std::map g_getResultFunc = { - {"ConfirmValue", ConfirmValueResult}, - {"BuildMemRef", BuildMemRefResult}, - {"DebugValue", DebugValueResult}, -+ {"IsLtoOptimize",IsLtoOptimizeResult}, -+ {"IsWholeProgram",IsWholeProgramResult}, - }; - - void PluginClient::GetIRTransResult(void *gccData, const string& funcName, const string& param) -diff --git a/lib/Translate/GimpleToPluginOps.cpp b/lib/Translate/GimpleToPluginOps.cpp -index 5ecab75..c4e5611 100644 ---- a/lib/Translate/GimpleToPluginOps.cpp -+++ b/lib/Translate/GimpleToPluginOps.cpp -@@ -529,6 +529,16 @@ void GimpleToPluginOps::RemoveEdge(uint64_t src, uint64_t dest) - remove_edge(e); - } - -+bool GimpleToPluginOps::IsLtoOptimize() -+{ -+ return in_lto_p; -+} -+ -+bool GimpleToPluginOps::IsWholeProgram() -+{ -+ return flag_whole_program; -+} -+ - FunctionOp GimpleToPluginOps::BuildFunctionOp(uint64_t functionId) - { - function *fn = reinterpret_cast(functionId); --- -2.27.0.windows.1 - diff --git a/0008-Pin-gcc-client-Fix-bug-for-BuildCallOp.patch b/0008-Pin-gcc-client-Fix-bug-for-BuildCallOp.patch deleted file mode 100644 index cb294ad..0000000 --- a/0008-Pin-gcc-client-Fix-bug-for-BuildCallOp.patch +++ /dev/null @@ -1,115 +0,0 @@ -From d8c42ec77001c29291a31484031cb7b5f2b52d48 Mon Sep 17 00:00:00 2001 -From: Mingchuan Wu -Date: Tue, 21 Feb 2023 16:53:09 +0800 -Subject: [PATCH 8/9] [Pin-gcc-client] Fix bug for BuildCallOp. Now we can - convert the function pointer. - - -diff --git a/include/Dialect/PluginOps.td b/include/Dialect/PluginOps.td -index bac84be..713623c 100644 ---- a/include/Dialect/PluginOps.td -+++ b/include/Dialect/PluginOps.td -@@ -85,12 +85,15 @@ def CallOp : Plugin_Op<"call", [ - The arguments list must match the arguments expected by the callee. - }]; - let arguments = (ins UI64Attr:$id, -- FlatSymbolRefAttr:$callee, -+ OptionalAttr:$callee, - Variadic:$inputs); - let results = (outs Optional:$result); - let builders = [ - OpBuilderDAG<(ins "uint64_t":$id, "StringRef":$callee, -- "ArrayRef":$arguments, "Type":$retType)> -+ "ArrayRef":$arguments, "Type":$retType)>, -+ OpBuilderDAG<(ins "uint64_t":$id, -+ "ArrayRef":$arguments, -+ "Type":$retType)> - ]; - let extraClassDeclaration = [{ - Type getResultType() { return this->getOperation()->getResult(0).getType(); } -diff --git a/lib/Dialect/PluginOps.cpp b/lib/Dialect/PluginOps.cpp -index da5f3f3..1ea3b5a 100644 ---- a/lib/Dialect/PluginOps.cpp -+++ b/lib/Dialect/PluginOps.cpp -@@ -146,6 +146,14 @@ void CallOp::build(OpBuilder &builder, OperationState &state, - if (retType != nullptr) state.addTypes(retType); - } - -+void CallOp::build(OpBuilder &builder, OperationState &state, -+ uint64_t id, ArrayRef arguments, Type retType) -+{ -+ state.addAttribute("id", builder.getI64IntegerAttr(id)); -+ state.addOperands(arguments); -+ if (retType != nullptr) state.addTypes(retType); -+} -+ - /// Return the callee of the generic call operation, this is required by the - /// call interface. - CallInterfaceCallable CallOp::getCallableForCallee() -diff --git a/lib/PluginClient/PluginJson.cpp b/lib/PluginClient/PluginJson.cpp -index ccadf10..966d35e 100755 ---- a/lib/PluginClient/PluginJson.cpp -+++ b/lib/PluginClient/PluginJson.cpp -@@ -341,7 +341,10 @@ Json::Value PluginJson::CallOpJsonSerialize(CallOp& data) - { - Json::Value item; - item["id"] = std::to_string(data.idAttr().getInt()); -- item["callee"] = data.callee().str(); -+ Optional calleeName = data.callee(); -+ if (calleeName) { -+ item["callee"] = calleeName->str(); -+ } - item["OperationName"] = data.getOperation()->getName().getStringRef().str(); - size_t opIdx = 0; - for (mlir::Value v : data.getArgOperands()) { -diff --git a/lib/Translate/GimpleToPluginOps.cpp b/lib/Translate/GimpleToPluginOps.cpp -index c4e5611..515e5e6 100644 ---- a/lib/Translate/GimpleToPluginOps.cpp -+++ b/lib/Translate/GimpleToPluginOps.cpp -@@ -645,10 +645,6 @@ uint64_t GimpleToPluginOps::CreateGphiNode(uint64_t argId, uint64_t blockId) - CallOp GimpleToPluginOps::BuildCallOp(uint64_t gcallId) - { - gcall *stmt = reinterpret_cast(gcallId); -- tree fndecl = gimple_call_fndecl(stmt); -- if (fndecl == NULL_TREE || DECL_NAME(fndecl) == NULL_TREE) { -- return nullptr; -- } - llvm::SmallVector ops; - ops.reserve(gimple_call_num_args(stmt)); - for (unsigned i = 0; i < gimple_call_num_args(stmt); i++) { -@@ -658,11 +654,18 @@ CallOp GimpleToPluginOps::BuildCallOp(uint64_t gcallId) - Value arg = TreeToValue(argId); - ops.push_back(arg); - } -- StringRef callName(IDENTIFIER_POINTER(DECL_NAME(fndecl))); - tree returnType = gimple_call_return_type(stmt); - PluginTypeBase rPluginType = typeTranslator.translateType((intptr_t)returnType); -- CallOp ret = builder.create(builder.getUnknownLoc(), -- gcallId, callName, ops, rPluginType); -+ tree fndecl = gimple_call_fndecl(stmt); -+ CallOp ret; -+ if (fndecl == NULL_TREE || DECL_NAME(fndecl) == NULL_TREE) { -+ ret = builder.create(builder.getUnknownLoc(), -+ gcallId, ops, rPluginType); -+ } else { -+ StringRef callName(IDENTIFIER_POINTER(DECL_NAME(fndecl))); -+ ret = builder.create(builder.getUnknownLoc(), -+ gcallId, callName, ops, rPluginType); -+ } - return ret; - } - -@@ -860,6 +863,10 @@ Value GimpleToPluginOps::TreeToValue(uint64_t treeId) - } else { - abort(); - } -+ opValue = builder.create( -+ builder.getUnknownLoc(), treeId, IDefineCode::IntCST, -+ readOnly, initAttr, rPluginType); -+ break; - } - case MEM_REF : { - tree operand0 = TREE_OPERAND(t, 0); --- -2.27.0.windows.1 - diff --git a/0009-Pin-gcc-client-Support-functiontype-structtype.eg.patch b/0009-Pin-gcc-client-Support-functiontype-structtype.eg.patch deleted file mode 100644 index 833bffd..0000000 --- a/0009-Pin-gcc-client-Support-functiontype-structtype.eg.patch +++ /dev/null @@ -1,721 +0,0 @@ -From 307831e1210fd9aa4453d774308f63812198b555 Mon Sep 17 00:00:00 2001 -From: d00573793 -Date: Tue, 21 Feb 2023 11:09:39 +0800 -Subject: [PATCH 9/9] [Pin-gcc-client] Support functiontype structtype.eg. - - -diff --git a/include/Dialect/PluginOps.td b/include/Dialect/PluginOps.td -index c48d002..64a0c3d 100644 ---- a/include/Dialect/PluginOps.td -+++ b/include/Dialect/PluginOps.td -@@ -32,15 +32,19 @@ def FunctionOp : Plugin_Op<"function", [NoSideEffect]> { - - let arguments = (ins UI64Attr:$id, - StrAttr:$funcName, -- OptionalAttr:$declaredInline); -+ OptionalAttr:$declaredInline, -+ TypeAttr:$type); - let regions = (region AnyRegion:$bodyRegion); - - // Add custom build methods for the operation. These method populates - // the `state` that MLIR uses to create operations, i.e. these are used when - // using `builder.create(...)`. - let builders = [ -- OpBuilderDAG<(ins "uint64_t":$id, "StringRef":$funcName, "bool":$declaredInline)> -+ OpBuilderDAG<(ins "uint64_t":$id, "StringRef":$funcName, "bool":$declaredInline, "Type":$type)> - ]; -+ let extraClassDeclaration = [{ -+ Type getResultType(); -+ }]; - } - - def LocalDeclOp : Plugin_Op<"declaration", [NoSideEffect]> { -diff --git a/include/Dialect/PluginTypes.h b/include/Dialect/PluginTypes.h -index 157b868..da16886 100644 ---- a/include/Dialect/PluginTypes.h -+++ b/include/Dialect/PluginTypes.h -@@ -78,6 +78,9 @@ namespace Detail { - struct PluginIntegerTypeStorage; - struct PluginFloatTypeStorage; - struct PluginPointerTypeStorage; -+ struct PluginTypeAndSizeStorage; -+ struct PluginFunctionTypeStorage; -+ struct PluginStructTypeStorage; - } - - class PluginIntegerType : public Type::TypeBase { -@@ -128,6 +131,60 @@ public: - unsigned isReadOnlyElem(); - }; // class PluginPointerType - -+class PluginArrayType : public Type::TypeBase { -+public: -+ using Base::Base; -+ -+ PluginTypeID getPluginTypeID (); -+ -+ static bool isValidElementType(Type type); -+ -+ static PluginArrayType get(MLIRContext *context, Type elementType, unsigned numElements); -+ -+ Type getElementType(); -+ -+ unsigned getNumElements(); -+}; // class PluginArrayType -+ -+class PluginFunctionType : public Type::TypeBase { -+public: -+ using Base::Base; -+ -+ PluginTypeID getPluginTypeID (); -+ -+ static bool isValidArgumentType(Type type); -+ -+ static bool isValidResultType(Type type); -+ -+ static PluginFunctionType get(MLIRContext *context, Type result, ArrayRef arguments); -+ -+ Type getReturnType(); -+ -+ unsigned getNumParams(); -+ -+ Type getParamType(unsigned i); -+ -+ ArrayRef getParams(); -+}; // class PluginFunctionType -+ -+class PluginStructType : public Type::TypeBase { -+public: -+ using Base::Base; -+ -+ PluginTypeID getPluginTypeID (); -+ -+ static bool isValidElementType(Type type); -+ -+ static PluginStructType get(MLIRContext *context, StringRef name, ArrayRef elements, ArrayRef elemNames); -+ -+ StringRef getName(); -+ -+ ArrayRef getBody(); -+ -+ ArrayRef getElementNames(); -+ -+}; // class PluginStructType -+ - class PluginVoidType : public Type::TypeBase { - public: - using Base::Base; -diff --git a/include/PluginClient/PluginJson.h b/include/PluginClient/PluginJson.h -index 91bd925..3c9fe1f 100755 ---- a/include/PluginClient/PluginJson.h -+++ b/include/PluginClient/PluginJson.h -@@ -59,7 +59,7 @@ public: - Json::Value MemOpJsonSerialize(mlir::Plugin::MemOp& data); - Json::Value SSAOpJsonSerialize(mlir::Plugin::SSAOp& data); - /* 将Type类型数据序列化 */ -- Json::Value TypeJsonSerialize(PluginIR::PluginTypeBase& type); -+ Json::Value TypeJsonSerialize(PluginIR::PluginTypeBase type); - PluginIR::PluginTypeBase TypeJsonDeSerialize(const string& data, mlir::MLIRContext &context); - /* 将整数型数据序列化 */ - void IntegerSerialize(int64_t data, string& out); -diff --git a/lib/Dialect/PluginDialect.cpp b/lib/Dialect/PluginDialect.cpp -index 527c076..69a0aa5 100644 ---- a/lib/Dialect/PluginDialect.cpp -+++ b/lib/Dialect/PluginDialect.cpp -@@ -38,6 +38,9 @@ void PluginDialect::initialize() - addTypes(); -diff --git a/lib/Dialect/PluginOps.cpp b/lib/Dialect/PluginOps.cpp -index 052ebfd..db2574c 100644 ---- a/lib/Dialect/PluginOps.cpp -+++ b/lib/Dialect/PluginOps.cpp -@@ -24,6 +24,7 @@ - - #include "Dialect/PluginDialect.h" - #include "Dialect/PluginOps.h" -+#include "Dialect/PluginTypes.h" - - #include "mlir/IR/Builders.h" - #include "mlir/IR/BuiltinTypes.h" -@@ -32,13 +33,20 @@ - using namespace mlir; - using namespace mlir::Plugin; - --void FunctionOp::build(mlir::OpBuilder &builder, mlir::OperationState &state, -- uint64_t id, StringRef funcName, bool declaredInline) -+void FunctionOp::build(OpBuilder &builder, OperationState &state, -+ uint64_t id, StringRef funcName, bool declaredInline, Type type) - { -- FunctionOp::build(builder, state, -- builder.getI64IntegerAttr(id), -- builder.getStringAttr(funcName), -- builder.getBoolAttr(declaredInline)); -+ state.addRegion(); -+ state.addAttribute("id", builder.getI64IntegerAttr(id)); -+ state.addAttribute("funcName", builder.getStringAttr(funcName)); -+ state.addAttribute("declaredInline", builder.getBoolAttr(declaredInline)); -+ if (type) state.addAttribute("type", TypeAttr::get(type)); -+} -+ -+Type FunctionOp::getResultType() -+{ -+ PluginIR::PluginFunctionType resultType = type().dyn_cast(); -+ return resultType; - } - - void LocalDeclOp::build(mlir::OpBuilder &builder, mlir::OperationState &state, -diff --git a/lib/Dialect/PluginTypes.cpp b/lib/Dialect/PluginTypes.cpp -index 396bf0f..329a2b6 100644 ---- a/lib/Dialect/PluginTypes.cpp -+++ b/lib/Dialect/PluginTypes.cpp -@@ -98,6 +98,80 @@ namespace Detail { - Type pointee; - unsigned readOnlyPointee; - }; -+ -+ struct PluginTypeAndSizeStorage : public TypeStorage { -+ using KeyTy = std::tuple; -+ -+ PluginTypeAndSizeStorage(const KeyTy &key) -+ : elementType(std::get<0>(key)), numElements(std::get<1>(key)) {} -+ -+ static PluginTypeAndSizeStorage *construct(TypeStorageAllocator &allocator, KeyTy key) -+ { -+ return new (allocator.allocate()) -+ PluginTypeAndSizeStorage(key); -+ } -+ -+ bool operator==(const KeyTy &key) const -+ { -+ return std::make_tuple(elementType, numElements) == key; -+ } -+ -+ Type elementType; -+ unsigned numElements; -+ }; -+ -+ struct PluginFunctionTypeStorage : public TypeStorage { -+ using KeyTy = std::tuple>; -+ -+ PluginFunctionTypeStorage(Type resultType, ArrayRef argumentTypes) -+ : resultType(resultType), argumentTypes(argumentTypes) {} -+ -+ static PluginFunctionTypeStorage *construct(TypeStorageAllocator &allocator, KeyTy key) -+ { -+ return new (allocator.allocate()) -+ PluginFunctionTypeStorage(std::get<0>(key), allocator.copyInto(std::get<1>(key))); -+ } -+ -+ static unsigned hashKey(const KeyTy &key) { -+ // LLVM doesn't like hashing bools in tuples. -+ return llvm::hash_combine(std::get<0>(key), std::get<1>(key)); -+ } -+ -+ bool operator==(const KeyTy &key) const -+ { -+ return std::make_tuple(resultType, argumentTypes) == key; -+ } -+ -+ Type resultType; -+ ArrayRef argumentTypes; -+ }; -+ -+ struct PluginStructTypeStorage : public TypeStorage { -+ using KeyTy = std::tuple, ArrayRef>; -+ -+ PluginStructTypeStorage(StringRef name, ArrayRef elements, ArrayRef elemNames) -+ : name(name), elements(elements), elemNames(elemNames) {} -+ -+ static PluginStructTypeStorage *construct(TypeStorageAllocator &allocator, KeyTy key) -+ { -+ return new (allocator.allocate()) -+ PluginStructTypeStorage(std::get<0>(key), allocator.copyInto(std::get<1>(key)), allocator.copyInto(std::get<2>(key))); -+ } -+ -+ static unsigned hashKey(const KeyTy &key) { -+ // LLVM doesn't like hashing bools in tuples. -+ return llvm::hash_combine(std::get<0>(key), std::get<1>(key), std::get<2>(key)); -+ } -+ -+ bool operator==(const KeyTy &key) const -+ { -+ return std::make_tuple(name, elements, elemNames) == key; -+ } -+ -+ StringRef name; -+ ArrayRef elements; -+ ArrayRef elemNames; -+ }; - } // namespace Detail - } // namespace PluginIR - -@@ -123,6 +197,15 @@ PluginTypeID PluginTypeBase::getPluginTypeID () - if (auto Ty = dyn_cast()) { - return Ty.getPluginTypeID (); - } -+ if (auto Ty = dyn_cast()) { -+ return Ty.getPluginTypeID (); -+ } -+ if (auto Ty = dyn_cast()) { -+ return Ty.getPluginTypeID (); -+ } -+ if (auto Ty = dyn_cast()) { -+ return Ty.getPluginTypeID (); -+ } - return PluginTypeID::UndefTyID; - } - -@@ -295,3 +378,108 @@ PluginPointerType PluginPointerType::get (MLIRContext *context, Type pointee, un - { - return Base::get(context, pointee, readOnlyPointee); - } -+ -+ -+// ===----------------------------------------------------------------------===// -+// Plugin Array Type -+// ===----------------------------------------------------------------------===// -+ -+PluginTypeID PluginArrayType::getPluginTypeID() -+{ -+ return PluginTypeID::ArrayTyID; -+} -+ -+bool PluginArrayType::isValidElementType(Type type) -+{ -+ return !type.isa(); -+} -+ -+PluginArrayType PluginArrayType::get(MLIRContext *context, Type elementType, unsigned numElements) -+{ -+ return Base::get(context, elementType, numElements); -+} -+ -+Type PluginArrayType::getElementType() -+{ -+ return getImpl()->elementType; -+} -+ -+unsigned PluginArrayType::getNumElements() -+{ -+ return getImpl()->numElements; -+} -+ -+// ===----------------------------------------------------------------------===// -+// Plugin Function Type -+// ===----------------------------------------------------------------------===// -+ -+PluginTypeID PluginFunctionType::getPluginTypeID() -+{ -+ return PluginTypeID::FunctionTyID; -+} -+ -+bool PluginFunctionType::isValidArgumentType(Type type) -+{ -+ return !type.isa(); -+} -+ -+bool PluginFunctionType::isValidResultType(Type type) { -+ return !type.isa(); -+} -+ -+PluginFunctionType PluginFunctionType::get(MLIRContext *context, Type result, ArrayRef arguments) -+{ -+ return Base::get(context, result, arguments); -+} -+ -+Type PluginFunctionType::getReturnType() -+{ -+ return getImpl()->resultType; -+} -+ -+unsigned PluginFunctionType::getNumParams() -+{ -+ return getImpl()->argumentTypes.size(); -+} -+ -+Type PluginFunctionType::getParamType(unsigned i) { -+ return getImpl()->argumentTypes[i]; -+} -+ -+ArrayRef PluginFunctionType::getParams() -+{ -+ return getImpl()->argumentTypes; -+} -+ -+// ===----------------------------------------------------------------------===// -+// Plugin Struct Type -+// ===----------------------------------------------------------------------===// -+ -+PluginTypeID PluginStructType::getPluginTypeID() -+{ -+ return PluginTypeID::StructTyID; -+} -+ -+bool PluginStructType::isValidElementType(Type type) { -+ return !type.isa(); -+} -+ -+PluginStructType PluginStructType::get(MLIRContext *context, StringRef name, ArrayRef elements, ArrayRef elemNames) -+{ -+ return Base::get(context, name, elements, elemNames); -+} -+ -+StringRef PluginStructType::getName() -+{ -+ return getImpl()->name; -+} -+ -+ArrayRef PluginStructType::getBody() -+{ -+ return getImpl()->elements; -+} -+ -+ArrayRef PluginStructType::getElementNames() -+{ -+ return getImpl()->elemNames; -+} -\ No newline at end of file -diff --git a/lib/PluginClient/PluginJson.cpp b/lib/PluginClient/PluginJson.cpp -index 22cd489..a9db475 100755 ---- a/lib/PluginClient/PluginJson.cpp -+++ b/lib/PluginClient/PluginJson.cpp -@@ -20,6 +20,7 @@ - This file contains the implementation of the PluginJson class. - */ - -+#include - #include - #include "PluginAPI/PluginClientAPI.h" - #include "PluginClient/PluginLog.h" -@@ -36,7 +37,7 @@ static uintptr_t GetID(Json::Value node) - return atol(id.c_str()); - } - --Json::Value PluginJson::TypeJsonSerialize(PluginIR::PluginTypeBase& type) -+Json::Value PluginJson::TypeJsonSerialize(PluginIR::PluginTypeBase type) - { - Json::Value root; - Json::Value operationObj; -@@ -48,6 +49,41 @@ Json::Value PluginJson::TypeJsonSerialize(PluginIR::PluginTypeBase& type) - ReTypeId = static_cast(type.getPluginTypeID()); - item["id"] = std::to_string(ReTypeId); - -+ if (auto Ty = type.dyn_cast()) { -+ std::string tyName = Ty.getName().str(); -+ item["structtype"] = tyName; -+ size_t paramIndex = 0; -+ ArrayRef paramsType = Ty.getBody(); -+ for (auto ty :paramsType) { -+ string paramStr = "elemType" + std::to_string(paramIndex++); -+ item["structelemType"][paramStr] = TypeJsonSerialize(ty.dyn_cast()); -+ } -+ paramIndex = 0; -+ ArrayRef paramsNames = Ty.getElementNames(); -+ for (auto name :paramsNames) { -+ string paramStr = "elemName" + std::to_string(paramIndex++); -+ item["structelemName"][paramStr] = name.str(); -+ } -+ } -+ -+ if (auto Ty = type.dyn_cast()) { -+ auto fnrestype = Ty.getReturnType().dyn_cast(); -+ item["fnreturntype"] = TypeJsonSerialize(fnrestype); -+ size_t paramIndex = 0; -+ ArrayRef paramsType = Ty.getParams(); -+ for (auto ty : Ty.getParams()) { -+ string paramStr = "argType" + std::to_string(paramIndex++); -+ item["fnargsType"][paramStr] = TypeJsonSerialize(ty.dyn_cast()); -+ } -+ } -+ -+ if (auto Ty = type.dyn_cast()) { -+ auto elemTy = Ty.getElementType().dyn_cast(); -+ item["elementType"] = TypeJsonSerialize(elemTy); -+ uint64_t elemNum = Ty.getNumElements(); -+ item["arraysize"] = std::to_string(elemNum); -+ } -+ - if (auto elemTy = type.dyn_cast()) { - auto baseTy = elemTy.getElementType().dyn_cast(); - item["elementType"] = TypeJsonSerialize(baseTy); -@@ -104,7 +140,40 @@ PluginIR::PluginTypeBase PluginJson::TypeJsonDeSerialize(const string& data, mli - } else if (id == static_cast(PluginIR::PointerTyID)) { - mlir::Type elemTy = TypeJsonDeSerialize(type["elementType"].toStyledString(), context); - baseType = PluginIR::PluginPointerType::get(&context, elemTy, type["elemConst"].asString() == "1" ? 1 : 0); -- } else { -+ } else if (id == static_cast(PluginIR::ArrayTyID)) { -+ mlir::Type elemTy = TypeJsonDeSerialize(type["elementType"].toStyledString(), context); -+ uint64_t elemNum = GetID(type["arraysize"]); -+ baseType = PluginIR::PluginArrayType::get(&context, elemTy, elemNum); -+ } else if (id == static_cast(PluginIR::FunctionTyID)) { -+ mlir::Type returnTy = TypeJsonDeSerialize(type["fnreturntype"].toStyledString(), context); -+ llvm::SmallVector typelist; -+ Json::Value::Members fnTypeNum = type["fnargsType"].getMemberNames(); -+ uint64_t argsNum = fnTypeNum.size(); -+ for (size_t paramIndex = 0; paramIndex < argsNum; paramIndex++) { -+ string Key = "argType" + std::to_string(paramIndex); -+ mlir::Type paramTy = TypeJsonDeSerialize(type["fnargsType"][Key].toStyledString(), context); -+ typelist.push_back(paramTy); -+ } -+ baseType = PluginIR::PluginFunctionType::get(&context, returnTy, typelist); -+ } else if (id == static_cast(PluginIR::StructTyID)) { -+ StringRef tyName = type["structtype"].toStyledString(); -+ llvm::SmallVector typelist; -+ Json::Value::Members elemTypeNum = type["structelemType"].getMemberNames(); -+ for (size_t paramIndex = 0; paramIndex < elemTypeNum.size(); paramIndex++) { -+ string Key = "elemType" + std::to_string(paramIndex); -+ mlir::Type paramTy = TypeJsonDeSerialize(type["structelemType"][Key].toStyledString(), context); -+ typelist.push_back(paramTy); -+ } -+ llvm::SmallVector names; -+ Json::Value::Members elemNameNum = type["structelemName"].getMemberNames(); -+ for (size_t paramIndex = 0; paramIndex < elemTypeNum.size(); paramIndex++) { -+ string Key = "elemName" + std::to_string(paramIndex); -+ StringRef elemName = type["structelemName"][Key].toStyledString(); -+ names.push_back(elemName); -+ } -+ baseType = PluginIR::PluginStructType::get(&context, tyName, typelist, names); -+ } -+ else { - if (PluginTypeId == PluginIR::VoidTyID) { - baseType = PluginIR::PluginVoidType::get(&context); - } -@@ -127,7 +196,6 @@ void PluginJson::FunctionOpJsonSerialize(vector& data, string& out) - - int i = 0; - string operation; -- - for (auto& d: data) { - item["id"] = std::to_string(d.idAttr().getInt()); - if (d.declaredInlineAttr().getValue()) { -@@ -136,6 +204,14 @@ void PluginJson::FunctionOpJsonSerialize(vector& data, string& out) - item["attributes"]["declaredInline"] = "0"; - } - item["attributes"]["funcName"] = d.funcNameAttr().getValue().str().c_str(); -+ -+ mlir::Type fnty = d.type(); -+ if (auto ty = fnty.dyn_cast()) { -+ if (auto retTy = ty.dyn_cast()) { -+ item["retType"] = TypeJsonSerialize(retTy); -+ } -+ } -+ - auto ®ion = d.getRegion(); - size_t bbIdx = 0; - for (auto &b : region) { -diff --git a/lib/Translate/GimpleToPluginOps.cpp b/lib/Translate/GimpleToPluginOps.cpp -index b5974aa..3e2321a 100644 ---- a/lib/Translate/GimpleToPluginOps.cpp -+++ b/lib/Translate/GimpleToPluginOps.cpp -@@ -536,9 +536,12 @@ FunctionOp GimpleToPluginOps::BuildFunctionOp(uint64_t functionId) - bool declaredInline = false; - if (DECL_DECLARED_INLINE_P(fn->decl)) - declaredInline = true; -+ tree returnType = TREE_TYPE(fn->decl); -+ PluginTypeBase rPluginType = typeTranslator.translateType((intptr_t)returnType); - auto location = builder.getUnknownLoc(); -+ auto Ty = rPluginType.dyn_cast(); - FunctionOp retOp = builder.create(location, functionId, -- funcName, declaredInline); -+ funcName, declaredInline, Ty); - auto& fr = retOp.bodyRegion(); - if (fn->cfg == nullptr) return retOp; - if (!ProcessBasicBlock((intptr_t)ENTRY_BLOCK_PTR_FOR_FN(fn), fr)) { -diff --git a/lib/Translate/TypeTranslation.cpp b/lib/Translate/TypeTranslation.cpp -index 7c7cff4..ad840f7 100644 ---- a/lib/Translate/TypeTranslation.cpp -+++ b/lib/Translate/TypeTranslation.cpp -@@ -45,10 +45,12 @@ - #include "ssa.h" - #include "output.h" - #include "langhooks.h" -+#include "print-tree.h" -+#include "stor-layout.h" - --using namespace mlir; - - namespace PluginIR { -+using namespace mlir; - namespace Detail { - /* Support for translating Plugin IR types to MLIR Plugin dialect types. */ - class TypeFromPluginIRTranslatorImpl { -@@ -77,6 +79,77 @@ private: - return false; - } - -+ unsigned getDomainIndex (tree type) -+ { -+ return tree_to_shwi(TYPE_MAX_VALUE(TYPE_DOMAIN(type)))+1; -+ } -+ -+ llvm::SmallVector getArgsType (tree type) -+ { -+ tree parmlist = TYPE_ARG_TYPES (type); -+ tree parmtype; -+ llvm::SmallVector typelist; -+ for (; parmlist; parmlist = TREE_CHAIN (parmlist)) -+ { -+ parmtype = TREE_VALUE (parmlist); -+ typelist.push_back(translatePrimitiveType(parmtype)); -+ } -+ return typelist; -+ } -+ -+ const char *getTypeName (tree type) -+ { -+ const char *tname = NULL; -+ -+ if (type == NULL) -+ { -+ return NULL; -+ } -+ -+ if (TYPE_NAME (type) != NULL) -+ { -+ if (TREE_CODE (TYPE_NAME (type)) == IDENTIFIER_NODE) -+ { -+ tname = IDENTIFIER_POINTER (TYPE_NAME (type)); -+ } -+ else if (DECL_NAME (TYPE_NAME (type)) != NULL) -+ { -+ tname = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); -+ } -+ } -+ return tname; -+ } -+ -+ llvm::SmallVector getElemType(tree type) -+ { -+ llvm::SmallVector typelist; -+ tree parmtype; -+ for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) -+ { -+ if (TREE_CODE (field) == FIELD_DECL) -+ { -+ parmtype = TREE_TYPE(field); -+ typelist.push_back(translatePrimitiveType(parmtype)); -+ } -+ } -+ return typelist; -+ } -+ -+ llvm::SmallVector getElemNames(tree type) -+ { -+ llvm::SmallVector names; -+ StringRef name; -+ for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) -+ { -+ if (TREE_CODE (field) == FIELD_DECL) -+ { -+ name = IDENTIFIER_POINTER ( DECL_NAME(field)); -+ names.push_back(name); -+ } -+ } -+ return names; -+ } -+ - /* Translates the given primitive, i.e. non-parametric in MLIR nomenclature, - type. */ - PluginTypeBase translatePrimitiveType (tree type) -@@ -93,12 +166,21 @@ private: - if (TREE_CODE(type) == POINTER_TYPE) - return PluginPointerType::get(&context, translatePrimitiveType(TREE_TYPE(type)), - TYPE_READONLY(TREE_TYPE(type)) ? 1 : 0); -+ if (TREE_CODE(type) == ARRAY_TYPE) -+ return PluginArrayType::get(&context,translatePrimitiveType(TREE_TYPE(type)), getDomainIndex(type)); -+ if (TREE_CODE(type) == FUNCTION_TYPE) { -+ llvm::SmallVector argsType = getArgsType(type); -+ return PluginFunctionType::get(&context, translatePrimitiveType(TREE_TYPE(type)),argsType); -+ } -+ if (TREE_CODE(type) == RECORD_TYPE) { -+ return PluginStructType::get(&context, getTypeName(type), getElemType(type), getElemNames(type)); -+ } - return PluginUndefType::get(&context); - } - - /* The context in which MLIR types are created. */ - mlir::MLIRContext &context; --}; -+}; // class TypeFromPluginIRTranslatorImpl - - /* Support for translating MLIR Plugin dialect types to Plugin IR types . */ - class TypeToPluginIRTranslatorImpl { -@@ -126,6 +208,16 @@ private: - return false; - } - -+ auto_vec getParamsType(PluginFunctionType Ty) -+ { -+ auto_vec paramTypes; -+ ArrayRef ArgsTypes = Ty.getParams(); -+ for (auto ty :ArgsTypes) { -+ paramTypes.safe_push(translatePrimitiveType(ty.dyn_cast())); -+ } -+ return paramTypes; -+ } -+ - tree translatePrimitiveType(PluginTypeBase type) - { - if (auto Ty = type.dyn_cast()) { -@@ -179,9 +271,50 @@ private: - TYPE_READONLY(elmTy) = elmConst ? 1 : 0; - return build_pointer_type(elmTy); - } -+ if (auto Ty = type.dyn_cast()) { -+ mlir::Type elmType = Ty.getElementType(); -+ auto ty = elmType.dyn_cast(); -+ tree elmTy = translatePrimitiveType(ty); -+ unsigned elmNum = Ty.getNumElements(); -+ tree index = build_index_type (size_int (elmNum)); -+ return build_array_type(elmTy, index); -+ } -+ if (auto Ty = type.dyn_cast()) { -+ Type resultType = Ty.getReturnType(); -+ tree returnType = translatePrimitiveType(resultType.dyn_cast()); -+ auto_vec paramTypes = getParamsType(Ty); -+ return build_function_type_array(returnType, paramTypes.length (), paramTypes.address ()); -+ } -+ if (auto Ty = type.dyn_cast()) { -+ ArrayRef elemTypes = Ty.getBody(); -+ ArrayRef elemNames = Ty.getElementNames(); -+ StringRef tyName = Ty.getName(); -+ unsigned fieldSize = elemNames.size(); -+ -+ tree fields[fieldSize]; -+ tree ret; -+ unsigned i; -+ -+ ret = make_node (RECORD_TYPE); -+ for (i = 0; i < fieldSize; i++) -+ { -+ mlir::Type elemTy = elemTypes[i]; -+ auto ty = elemTy.dyn_cast(); -+ tree elmType = translatePrimitiveType(ty); -+ fields[i] = build_decl (UNKNOWN_LOCATION, FIELD_DECL, get_identifier (elemNames[i].str().c_str()), elmType); -+ DECL_CONTEXT (fields[i]) = ret; -+ if (i) DECL_CHAIN (fields[i - 1]) = fields[i]; -+ } -+ tree typeDecl = build_decl (input_location, TYPE_DECL, get_identifier (tyName.str().c_str()), ret); -+ DECL_ARTIFICIAL (typeDecl) = 1; -+ TYPE_FIELDS (ret) = fields[0]; -+ TYPE_NAME (ret) = typeDecl; -+ layout_type (ret); -+ return ret; -+ } - return NULL; - } --}; -+}; // class TypeToPluginIRTranslatorImpl - - } // namespace Detail - } // namespace PluginIR --- -2.27.0.windows.1 - diff --git a/pin-gcc-client-0.4.0.tar.gz b/pin-gcc-client-0.4.0.tar.gz deleted file mode 100644 index 745c5069206d8e82ccd9a5a28eecb1a618128880..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 52554 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 iJd - 0.4.0-2 +* Sun Feb 26 2023 dingguangya - 0.4.1-1 - Type:Update - ID:NA - SUG:NA -- DESC:Sync patch from openEuler/pin-gcc-client +- DESC:Update to v0.4.1 * Tue Dec 20 2022 zhaowenyu <804544223@qq.com> - 0.4.0-1 - Type:Update -- Gitee