From 4cb0f46faefec7e09bfdd9739aa835b1d04496fa Mon Sep 17 00:00:00 2001 From: Dedmen Miller Date: Mon, 8 Apr 2019 19:34:22 +0200 Subject: [PATCH] Add Optimizer --- CMakeLists.txt | 8 +- CMakeSettings.json | 28 ++ src/compiledCode.hpp | 71 ++++- src/main.cpp | 8 +- src/optimizer/optimizer.cpp | 5 + src/optimizer/optimizer.h | 11 + src/optimizer/optimizerModuleBase.cpp | 268 ++++++++++++++++++ src/optimizer/optimizerModuleBase.hpp | 88 ++++++ src/optimizer/optimizerModuleConstantFold.cpp | 205 ++++++++++++++ src/optimizer/optimizerModuleConstantFold.hpp | 10 + src/scriptCompiler.cpp | 208 +++++++++++++- src/scriptCompiler.hpp | 5 + src/scriptSerializer.cpp | 62 ++-- 13 files changed, 943 insertions(+), 34 deletions(-) create mode 100644 CMakeSettings.json create mode 100644 src/optimizer/optimizer.cpp create mode 100644 src/optimizer/optimizer.h create mode 100644 src/optimizer/optimizerModuleBase.cpp create mode 100644 src/optimizer/optimizerModuleBase.hpp create mode 100644 src/optimizer/optimizerModuleConstantFold.cpp create mode 100644 src/optimizer/optimizerModuleConstantFold.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ce88614..1e8bd78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,10 @@ endif() file(GLOB_RECURSE SOURCES_ASC "src/*.hpp" "src/*.cpp" "${LIBRARY_PATH_B64}/base64.cpp") SOURCE_GROUP("src" FILES ${SOURCES_ASC}) + +file(GLOB_RECURSE SOURCES_ASC_OPT "src/optimizer/*.hpp" "src/optimizer/*.cpp") +SOURCE_GROUP("src" FILES ${SOURCES_ASC_OPT}) + #zstd SET(ZSTD_BUILD_PROGRAMS OFF CACHE BOOL "no programs" FORCE) set(ZSTD_USE_STATIC_RUNTIME ON CACHE BOOL "yes" FORCE) @@ -87,7 +91,7 @@ add_definitions(/DLOADFILE_CACHE) #SQF VM fileio.cpp cache -add_executable("ArmaScriptCompiler" ${SOURCES_ASC} ${SOURCES_SQFVM}) +add_executable("ArmaScriptCompiler" ${SOURCES_ASC} ${SOURCES_SQFVM} ${SOURCES_ASC_OPT}) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${INCLUDE_PATH_ZSTD} ${LIBRARY_PATH_B64}) # ${INCLUDE_PATH_ZSTD} ${INCLUDE_PATH_SQFVM} @@ -121,6 +125,7 @@ if(CMAKE_COMPILER_IS_GNUCXX) target_link_options("ArmaScriptCompiler" PRIVATE "-m32" "-fPIC" "-static" "-static-libgcc" "-static-libstdc++" "$<$:-Wl,--gc-sections>" "$<$:-Wl,--strip-all>" + "-Wl,--stack,4194304" #STack size ) set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") @@ -136,6 +141,7 @@ else() "$<$:/Oi>" "$<$:/Ot>" "$<$:/GL>" + "/F4194304" #Stack size ) target_compile_options( diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 0000000..01191ed --- /dev/null +++ b/CMakeSettings.json @@ -0,0 +1,28 @@ +{ + "configurations": [ + { + "name": "x64-Release", + "generator": "Ninja", + "configurationType": "RelWithDebInfo", + "buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}", + "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "inheritEnvironments": [ "msvc_x64_x64" ], + "variables": [] + }, + { + "name": "x64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}", + "installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "inheritEnvironments": [ "msvc_x64_x64" ], + "variables": [] + } + ] +} \ No newline at end of file diff --git a/src/compiledCode.hpp b/src/compiledCode.hpp index 8545fc6..c67237c 100644 --- a/src/compiledCode.hpp +++ b/src/compiledCode.hpp @@ -38,6 +38,23 @@ enum class InstructionType { makeArray }; + +static std::string_view instructionTypeToString(InstructionType type) { + switch (type) { + case InstructionType::endStatement: return "endStatement"sv; + case InstructionType::push: return "push"sv; + case InstructionType::callUnary: return "callUnary"sv; + case InstructionType::callBinary: return "callBinary"sv; + case InstructionType::callNular: return "callNular"sv; + case InstructionType::assignTo: return "assignTo"sv; + case InstructionType::assignToLocal: return "assignToLocal"sv; + case InstructionType::getVariable: return "getVariable"sv; + case InstructionType::makeArray: return "makeArray"sv; + default: __debugbreak(); + } +} + + struct ScriptInstruction { InstructionType type; size_t offset; @@ -51,7 +68,8 @@ enum class ConstantType { code, string, scalar, - boolean + boolean, + array }; struct ScriptCodePiece { @@ -70,12 +88,31 @@ struct ScriptCodePiece { contentSplit.offset = offset; } ScriptCodePiece(std::vector&& c, uint64_t content) : code(c), contentString(content) {} + //ScriptCodePiece(ScriptCodePiece&& o) noexcept : code(std::move(o.code)), contentString(o.contentString) {} + //ScriptCodePiece(const ScriptCodePiece& o) noexcept : code(o.code), contentString(o.contentString) {} + // + //ScriptCodePiece& operator=(ScriptCodePiece&& o) noexcept { + // code = std::move(o.code); + // contentString = o.contentString; + // return *this; + //} + //ScriptCodePiece& operator=(const ScriptCodePiece& o) noexcept { + // code = o.code; + // contentString = o.contentString; + // return *this; + //} + ScriptCodePiece(): contentString(0) {} - }; +struct ScriptConstantArray; + +using ScriptConstant = std::variant; + +struct ScriptConstantArray { + std::vector content; +}; -using ScriptConstant = std::variant; constexpr ConstantType getConstantType(const ScriptConstant& c) { switch (c.index()) { @@ -83,11 +120,11 @@ constexpr ConstantType getConstantType(const ScriptConstant& c) { case 1: return ConstantType::string; case 2: return ConstantType::scalar; case 3: return ConstantType::boolean; + case 4: return ConstantType::array; } __debugbreak(); } - struct CompiledCodeData { uint32_t version{1}; uint64_t codeIndex; //index to main code in constants @@ -100,4 +137,28 @@ struct CompiledCodeData { //#TODO compress constants, don't have duplicates for a number or string -}; \ No newline at end of file +}; + + + +template +class Singleton { + Singleton(const Singleton&) = delete; + Singleton(Singleton&&) = delete; + Singleton& operator=(const Singleton&) = delete; + Singleton& operator=(Singleton&&) = delete; +public: + static __forceinline T& get() noexcept { + return _singletonInstance; + } + static void release() { + } +protected: + Singleton() noexcept {} + static T _singletonInstance; + static bool _initialized; +}; +template +T Singleton::_singletonInstance; +template +bool Singleton::_initialized = false; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 8e79a70..7aab5da 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,8 +36,8 @@ void compileRecursive(std::filesystem::path inputDir) { if (i->path().filename().extension() == ".sqf"sv) { if (i->path().filename() == "fnc_zeusAttributes.sqf") continue; //Hard ignore for missing include file //if (i->path().filename() != "test.sqf") continue; //Hard ignore for missing include file - //if (i->path().string().find("3DEN") != std::string::npos) continue; //CBA trying to format a code piece - //if (i->path().filename().string().find("initDisplay3DEN") == std::string::npos) continue; //Hard ignore unit tests + if (i->path().string().find("keybinding") == std::string::npos) continue; //CBA trying to format a code piece + if (i->path().filename().string().find("XEH_preStart") == std::string::npos) continue; //Hard ignore unit tests tasks.emplace(i->path()); } } @@ -61,12 +61,12 @@ void processFile(ScriptCompiler& comp, std::filesystem::path path) { //ScriptSerializer::compiledToBinary(compiledData, output); outputFile.flush(); - std::istringstream data2(data, std::istringstream::binary); + //std::istringstream data2(data, std::istringstream::binary); //auto res = ScriptSerializer::binaryToCompiledCompressed(data2); - auto outputPath2 = path.parent_path() / (path.stem().string() + ".sqfa"); + //auto outputPath2 = path.parent_path() / (path.stem().string() + ".sqfa"); //std::ofstream output2(outputPath2, std::ofstream::binary); //ScriptSerializer::compiledToHumanReadable(compiledData, output2); //output2.flush(); diff --git a/src/optimizer/optimizer.cpp b/src/optimizer/optimizer.cpp new file mode 100644 index 0000000..421bd41 --- /dev/null +++ b/src/optimizer/optimizer.cpp @@ -0,0 +1,5 @@ +#include "optimizer.h" + +void Optimizer::optimize(Node& node) { + optimizeConstantFold(node); +} diff --git a/src/optimizer/optimizer.h b/src/optimizer/optimizer.h new file mode 100644 index 0000000..fc0b735 --- /dev/null +++ b/src/optimizer/optimizer.h @@ -0,0 +1,11 @@ +#pragma once +#include "optimizerModuleConstantFold.hpp" + + +class Optimizer : public OptimizerModuleConstantFold { +public: + + void optimize(Node& node); + + +}; \ No newline at end of file diff --git a/src/optimizer/optimizerModuleBase.cpp b/src/optimizer/optimizerModuleBase.cpp new file mode 100644 index 0000000..63a6591 --- /dev/null +++ b/src/optimizer/optimizerModuleBase.cpp @@ -0,0 +1,268 @@ +#include "optimizerModuleBase.hpp" +#include +#include "parsesqf.h" +#include +#include "stringdata.h" +#include + + +void OptimizerModuleBase::Node::dumpTree(std::ostream& output, size_t indent) const { + for (int i = 0; i < indent; ++i) { + output << " "; + } + output << instructionTypeToString(type) << ":" << line << "\n"; + + for (auto& it : children) { + it.dumpTree(output, indent + 1); + } +} + +bool OptimizerModuleBase::Node::buildConstState() { + return false; + //switch (type) { + // + // case InstructionType::push: return constant = true; + // case InstructionType::assignTo: return false; + // case InstructionType::assignToLocal: return false; + // case InstructionType::getVariable: return false; + // case InstructionType::makeArray: return constant = true; + //} + // + //if (type == InstructionType::push) { + // return constant = true; + //} + // + // + // + //bool childrenConstant = std::all_of(children.begin(), children.end(), [](Node& it) { + // return it.buildConstState(); + // }); + // + //bool meIsConst = false; + // + //switch (type) { + // + // case InstructionType::endStatement: return constant = childrenConstant; + // case InstructionType::callUnary: return constant = canUnaryBeConst.anyOf(*this, childrenConstant); + // case InstructionType::callBinary: return constant = canBinaryBeConst.anyOf(*this, childrenConstant); + // case InstructionType::callNular: return constant = canNularBeConst.anyOf(*this, childrenConstant); + //} + + + + +} + +bool OptimizerModuleBase::Node::areChildrenConstant() const { + return std::all_of(children.begin(), children.end(), [](const Node& it) { + return it.constant; + }); +} + +std::vector OptimizerModuleBase::Node::bottomUpFlatten() { + std::vector> result; + std::queue myQueue; + myQueue.push(this); + size_t totalNodeCount = 0; + int currentLevelNodeNum = 1;//used to record num of nodes in current level + int nextLevelNodeNum = 0;//used to record num of nodes in next level + std::vector level; + while (!myQueue.empty()) { + + OptimizerModuleBase::Node* temp = myQueue.front(); + myQueue.pop(); + level.push_back(temp); + totalNodeCount++; + currentLevelNodeNum--; + for (auto& it : temp->children) { + myQueue.push(&it); + nextLevelNodeNum++; + } + if (currentLevelNodeNum == 0) {//if we have traversed current level, turn to next level + result.emplace_back(std::move(level));//push the current level into result + currentLevelNodeNum = nextLevelNodeNum;//assign next level node num to current + nextLevelNodeNum = 0;//set next level num to 0 + } + } + std::reverse(result.begin(), result.end()); + std::vector endResult; + endResult.reserve(totalNodeCount); + for (auto& it : result) + endResult.insert(endResult.end(), it.begin(), it.end()); + + return endResult; +} + +OptimizerModuleBase::Node OptimizerModuleBase::nodeFromAST(const astnode& input) { + auto nodeType = static_cast(input.kind); + switch (nodeType) { + + case sqf::parse::sqf::sqfasttypes::ASSIGNMENT: + case sqf::parse::sqf::sqfasttypes::ASSIGNMENTLOCAL: { + Node newNode; + + newNode.type = nodeType == sqf::parse::sqf::sqfasttypes::ASSIGNMENT ? InstructionType::assignTo : InstructionType::assignToLocal; + newNode.file = input.file; + newNode.line = input.line; + newNode.offset = input.offset; + auto varname = input.children[0].content; + std::transform(varname.begin(), varname.end(), varname.begin(), ::tolower); + newNode.value = std::move(varname); + + newNode.children.emplace_back(nodeFromAST(input.children[1])); + return newNode; + } + case sqf::parse::sqf::sqfasttypes::BEXP1: + case sqf::parse::sqf::sqfasttypes::BEXP2: + case sqf::parse::sqf::sqfasttypes::BEXP3: + case sqf::parse::sqf::sqfasttypes::BEXP4: + case sqf::parse::sqf::sqfasttypes::BEXP5: + case sqf::parse::sqf::sqfasttypes::BEXP6: + case sqf::parse::sqf::sqfasttypes::BEXP7: + case sqf::parse::sqf::sqfasttypes::BEXP8: + case sqf::parse::sqf::sqfasttypes::BEXP9: + case sqf::parse::sqf::sqfasttypes::BEXP10: + case sqf::parse::sqf::sqfasttypes::BINARYEXPRESSION: { + Node newNode; + + newNode.type = InstructionType::callBinary; + newNode.file = input.file; + newNode.line = input.line; + newNode.offset = input.offset; + auto varname = input.children[1].content; + std::transform(varname.begin(), varname.end(), varname.begin(), ::tolower); + newNode.value = std::move(varname); + + newNode.children.emplace_back(nodeFromAST(input.children[0])); + newNode.children.emplace_back(nodeFromAST(input.children[2])); + + return newNode; + } + case sqf::parse::sqf::sqfasttypes::BINARYOP: __debugbreak(); break; + case sqf::parse::sqf::sqfasttypes::PRIMARYEXPRESSION: __debugbreak(); break; + case sqf::parse::sqf::sqfasttypes::NULAROP: { + Node newNode; + + newNode.type = InstructionType::callNular; + newNode.file = input.file; + newNode.line = input.line; + newNode.offset = input.offset; + auto varname = input.content; + std::transform(varname.begin(), varname.end(), varname.begin(), ::tolower); + newNode.value = std::move(varname); + return newNode; + } + case sqf::parse::sqf::sqfasttypes::UNARYEXPRESSION: { + Node newNode; + + newNode.type = InstructionType::callUnary; + newNode.file = input.file; + newNode.line = input.line; + newNode.offset = input.offset; + auto varname = input.children[0].content; + std::transform(varname.begin(), varname.end(), varname.begin(), ::tolower); + newNode.value = std::move(varname); + auto subEl = nodeFromAST(input.children[1]); + + newNode.children.emplace_back(std::move(subEl)); + return newNode; + } + case sqf::parse::sqf::sqfasttypes::UNARYOP: __debugbreak(); break; + case sqf::parse::sqf::sqfasttypes::NUMBER: + case sqf::parse::sqf::sqfasttypes::HEXNUMBER: { + float val; + auto res = + (nodeType == sqf::parse::sqf::sqfasttypes::HEXNUMBER) ? + std::from_chars(input.content.data() + 2, input.content.data() + input.content.size(), val, std::chars_format::hex) + : + std::from_chars(input.content.data(), input.content.data() + input.content.size(), val); + if (res.ec == std::errc::invalid_argument) { + throw std::runtime_error("invalid scalar at: " + input.file + ":" + std::to_string(input.line)); + } + else if (res.ec == std::errc::result_out_of_range) { + throw std::runtime_error("scalar out of range at: " + input.file + ":" + std::to_string(input.line)); + } + + + Node newNode; + + newNode.type = InstructionType::push; + newNode.file = input.file; + newNode.line = input.line; + newNode.offset = input.offset; + newNode.value = val; + return newNode; + } + case sqf::parse::sqf::sqfasttypes::VARIABLE: { + Node newNode; + + newNode.type = InstructionType::getVariable; + newNode.file = input.file; + newNode.line = input.line; + newNode.offset = input.offset; + auto varname = input.content; + std::transform(varname.begin(), varname.end(), varname.begin(), ::tolower); + newNode.value = std::move(varname); + return newNode; + } + case sqf::parse::sqf::sqfasttypes::STRING: { + Node newNode; + + newNode.type = InstructionType::push; + newNode.file = input.file; + newNode.line = input.line; + newNode.offset = input.offset; + newNode.value = sqf::stringdata::parse_from_sqf(input.content); + return newNode; + } + case sqf::parse::sqf::sqfasttypes::CODE: { + Node newNode; + + newNode.type = InstructionType::push; + newNode.file = input.file; + newNode.line = input.line; + newNode.offset = input.offset; + newNode.value = ScriptCodePiece({}, input.length - 2, input.offset + 1);//instructions are empty as they are in node children + + for (auto& it : input.children) { + newNode.children.emplace_back(nodeFromAST(it)); + } + + return newNode; + } + case sqf::parse::sqf::sqfasttypes::ARRAY: { + + Node newNode; + + newNode.type = InstructionType::makeArray; + newNode.file = input.file; + newNode.line = input.line; + newNode.offset = input.offset; + for (auto& it : input.children) { + newNode.children.emplace_back(nodeFromAST(it)); + } + + return newNode; + } + //case sqf::parse::sqf::sqfasttypes::NA: + //case sqf::parse::sqf::sqfasttypes::SQF: + //case sqf::parse::sqf::sqfasttypes::STATEMENT: + //case sqf::parse::sqf::sqfasttypes::BRACKETS: + // for (auto& it : node.children) + // stuffAST(output, instructions, it); + default: { + + Node newNode; + + newNode.type = InstructionType::endStatement; //just a dummy + newNode.file = input.file; + newNode.line = input.line; + newNode.offset = input.offset; + for (auto& it : input.children) { + newNode.children.emplace_back(nodeFromAST(it)); + } + return newNode; + } + } + __debugbreak(); //should never reach +} diff --git a/src/optimizer/optimizerModuleBase.hpp b/src/optimizer/optimizerModuleBase.hpp new file mode 100644 index 0000000..8b37ccd --- /dev/null +++ b/src/optimizer/optimizerModuleBase.hpp @@ -0,0 +1,88 @@ +#pragma once +#include "../compiledCode.hpp" +#include + +struct astnode; + +template +class Signal; + +template +class Signal { +private: + typedef std::function Slot; + +public: + void connect(Slot slot) { + slots.push_back(slot); + } + + std::vector operator() (Args&&... args) const { + return emit(std::forward(args)...); + } + std::vector emit(Args&&... args) const { + std::vector returnData; + if (slots.empty()) + return; + for (auto& slot : slots) { + returnData.push_back(slot(std::forward(args)...)); + } + return returnData; + } + bool anyOf(Args&&... args) const { + for (auto& slot : slots) { + if (slot(std::forward(args)...)) return true; + } + return false; + } + void removeAllSlots() { + slots.clear(); + } +private: + std::vector slots{}; +}; + +class OptimizerModuleBase { +public: + class Node { + public: + Node() {} + Node(Node&& mv) noexcept { + type = mv.type; + file = std::move(mv.file); + line = mv.line; + offset = mv.offset; + value = std::move(mv.value); + children = std::move(mv.children); + } + + InstructionType type; + + std::string file; + size_t line; + size_t offset; + + ScriptConstant value; + std::vector children; + + void dumpTree(std::ostream& output, size_t indent) const; + bool buildConstState();//#TODO remove + bool areChildrenConstant() const; + + + + std::vector bottomUpFlatten(); + + + bool constant = false; + }; + + static Node nodeFromAST(const astnode& input); //#TODO move to optimizer + + + Signal canBinaryBeConst;//#TODO remove + Signal canUnaryBeConst; + Signal canNularBeConst; + + +}; diff --git a/src/optimizer/optimizerModuleConstantFold.cpp b/src/optimizer/optimizerModuleConstantFold.cpp new file mode 100644 index 0000000..9612a6c --- /dev/null +++ b/src/optimizer/optimizerModuleConstantFold.cpp @@ -0,0 +1,205 @@ +#include "optimizerModuleConstantFold.hpp" +#include +#include + +class OptimizerConstantFoldActionMap : public Singleton { +public: + + OptimizerConstantFoldActionMap() { + setupBinary(); + setupUnary(); + setupNulary(); + } + + void processBinary(OptimizerModuleBase::Node& node) { + auto& cmdName = std::get(node.value); + auto found = binaryActions.find(cmdName); + if (found != binaryActions.end()) + found->second(node); + } + + void processUnary(OptimizerModuleBase::Node& node) { + auto& cmdName = std::get(node.value); + auto found = unaryActions.find(cmdName); + if (found != unaryActions.end()) + found->second(node); + } + + void processNulary(OptimizerModuleBase::Node& node) { + auto& cmdName = std::get(node.value); + auto found = nularyActions.find(cmdName); + if (found != nularyActions.end()) + found->second(node); + } + + +private: + + void setupBinary() { + binaryActions["else"] = [](OptimizerModuleBase::Node & node) -> void { + node.type = InstructionType::push; + node.constant = true; + + node.value = ScriptConstantArray(); //dummy. Children are the contents + }; + + //math + + + binaryActions["+"] = [](OptimizerModuleBase::Node & node) -> void { + float leftArg = std::get(node.children[0].value); + float rightArg = std::get(node.children[1].value); + + node.type = InstructionType::push; + node.children.clear(); + node.constant = true; + node.value = leftArg + rightArg; + }; + + binaryActions["-"] = [](OptimizerModuleBase::Node & node) -> void { + float leftArg = std::get(node.children[0].value); + float rightArg = std::get(node.children[1].value); + + node.type = InstructionType::push; + node.children.clear(); + node.constant = true; + node.value = leftArg - rightArg; + }; + + binaryActions["/"] = [](OptimizerModuleBase::Node & node) -> void { + float leftArg = std::get(node.children[0].value); + float rightArg = std::get(node.children[1].value); + + node.type = InstructionType::push; + node.children.clear(); + node.constant = true; + node.value = leftArg / rightArg; + }; + binaryActions["*"] = [](OptimizerModuleBase::Node & node) -> void { + float leftArg = std::get(node.children[0].value); + float rightArg = std::get(node.children[1].value); + + node.type = InstructionType::push; + node.children.clear(); + node.constant = true; + node.value = leftArg * rightArg; + }; + + binaryActions["mod"] = [](OptimizerModuleBase::Node & node) -> void { + float leftArg = std::get(node.children[0].value); + float rightArg = std::get(node.children[1].value); + + node.type = InstructionType::push; + node.children.clear(); + node.constant = true; + node.value = fmodf(leftArg, rightArg); + }; + + } + + void setupUnary() { + unaryActions["sqrt"] = [](OptimizerModuleBase::Node & node) -> void { + float rightArg = std::get(node.children[0].value); + + node.type = InstructionType::push; + node.children.clear(); + node.constant = true; + node.value = sqrt(rightArg); + }; + + unaryActions["!"] = [](OptimizerModuleBase::Node & node) -> void { + bool rightArg = std::get(node.children[0].value); + + node.type = InstructionType::push; + node.children.clear(); + node.constant = true; + node.value = !rightArg; + }; + } + + void setupNulary() { + nularyActions["true"] = [](OptimizerModuleBase::Node & node) -> void { + node.type = InstructionType::push; + node.children.clear(); + node.constant = true; + node.value = true; + }; + + nularyActions["false"] = [](OptimizerModuleBase::Node & node) -> void { + node.type = InstructionType::push; + node.children.clear(); + node.constant = true; + node.value = false; + }; + } + + std::unordered_map> binaryActions; + std::unordered_map> unaryActions; + std::unordered_map> nularyActions; +}; + + + + + + +void OptimizerModuleConstantFold::optimizeConstantFold(Node& node) { + + auto worklist = node.bottomUpFlatten(); + + for (auto& it : worklist) { + processNode(*it); + } + + + + +} + +void OptimizerModuleConstantFold::processNode(Node& node) { + + switch (node.type) { + case InstructionType::endStatement: break; + case InstructionType::push: { + node.constant = true; + }break; + case InstructionType::callUnary: { + if (!node.areChildrenConstant()) break; + OptimizerConstantFoldActionMap::get().processUnary(node); + + } break; + case InstructionType::callBinary: { + if (!node.areChildrenConstant()) break; + OptimizerConstantFoldActionMap::get().processBinary(node); + + } break; + case InstructionType::callNular: { + if (!node.areChildrenConstant()) break; + OptimizerConstantFoldActionMap::get().processNulary(node); + } break; + case InstructionType::assignTo: break; + case InstructionType::assignToLocal: break; + case InstructionType::getVariable: break; + case InstructionType::makeArray: { + if (node.areChildrenConstant()) {//#TODO when converting to ASM check again if all elements are push + bool allPush = std::all_of(node.children.begin(), node.children.end(), [](const Node & it) + { + return it.type == InstructionType::push; + }); + if (!allPush) { + std::stringstream buf; + node.dumpTree(buf, 0); + auto str = buf.str(); + __debugbreak(); + } + + node.value = ScriptConstantArray(); //dummy. Children are the contents + node.type = InstructionType::push; + node.constant = true; + } + } break; + default: ; + } + + +} diff --git a/src/optimizer/optimizerModuleConstantFold.hpp b/src/optimizer/optimizerModuleConstantFold.hpp new file mode 100644 index 0000000..c5ff06a --- /dev/null +++ b/src/optimizer/optimizerModuleConstantFold.hpp @@ -0,0 +1,10 @@ +#pragma once +#include "optimizerModuleBase.hpp" + +class OptimizerModuleConstantFold : public virtual OptimizerModuleBase { +public: + void optimizeConstantFold(Node& node); + +private: + void processNode(Node& node); +}; \ No newline at end of file diff --git a/src/scriptCompiler.cpp b/src/scriptCompiler.cpp index 138d298..89f8c12 100644 --- a/src/scriptCompiler.cpp +++ b/src/scriptCompiler.cpp @@ -11,6 +11,9 @@ #include #include #include "stringdata.h" +#include "optimizer/optimizerModuleBase.hpp" +#include "optimizer/optimizer.h" +#include "scriptSerializer.hpp" std::once_flag commandMapInitFlag; ScriptCompiler::ScriptCompiler(const std::vector& includePaths) { @@ -64,21 +67,62 @@ CompiledCodeData ScriptCompiler::compileScript(std::filesystem::path file) { CompiledCodeData stuff; CompileTempData temp; ScriptCodePiece mainCode; + + + { + auto node = OptimizerModuleBase::nodeFromAST(ast); + + std::ofstream nodeo("P:\\node.txt"); + node.dumpTree(nodeo, 0); + nodeo.close(); + + auto res = node.bottomUpFlatten(); + + Optimizer opt; + + opt.optimize(node); + + std::ofstream nodeop("P:\\nodeOpt.txt"); + node.dumpTree(nodeop, 0); + nodeop.close(); + + CompiledCodeData stuff; + CompileTempData temp; + ScriptCodePiece mainCode; + + + ASTToInstructions(stuff, temp, mainCode.code, node); + mainCode.contentString = stuff.constants.size(); + stuff.constants.emplace_back(std::move(preprocessedScript)); + stuff.codeIndex = stuff.constants.size(); + stuff.constants.emplace_back(std::move(mainCode)); + + + std::ofstream output2("P:\\outOpt.sqfa", std::ofstream::binary); + ScriptSerializer::compiledToHumanReadable(stuff, output2); + output2.flush(); + } + + + + + ASTToInstructions(stuff, temp, mainCode.code, ast); mainCode.contentString = stuff.constants.size(); stuff.constants.emplace_back(std::move(preprocessedScript)); stuff.codeIndex = stuff.constants.size(); stuff.constants.emplace_back(std::move(mainCode)); - //std::shared_ptr cs = std::make_shared(vm->missionnamespace()); - //vm->parse_sqf(vm->stack(), preprocessedScript, cs, "I:\\ACE3\\addons\\advanced_ballistics\\functions\\fnc_readWeaponDataFromConfig.sqf"); - //std::ofstream out("P:\\out.sqfa"); - //printSQFASM(stuff, out); //see main.cpp if you wanna do that. ScriptSerializer can serialize to ASM + + //auto outputPath2 = file.parent_path() / (file.stem().string() + ".sqfa"); + //std::ofstream output2(outputPath2, std::ofstream::binary); + std::ofstream output2("P:\\outOrig.sqfa", std::ofstream::binary); + ScriptSerializer::compiledToHumanReadable(stuff, output2); + output2.flush(); return stuff; } void ScriptCompiler::ASTToInstructions(CompiledCodeData& output, CompileTempData& temp, std::vector& instructions, const astnode& node) const { - auto getFileIndex = [&](const std::string& filename) -> uint8_t { auto found = temp.fileLoc.find(filename); @@ -104,7 +148,7 @@ void ScriptCompiler::ASTToInstructions(CompiledCodeData& output, CompileTempData nodeType == sqf::parse::sqf::sqfasttypes::ASSIGNMENT ? InstructionType::assignTo : - InstructionType::assignToLocal//#TODO tolower + InstructionType::assignToLocal , node.offset, getFileIndex(node.file), node.line, varname }); } break; @@ -259,6 +303,158 @@ void ScriptCompiler::ASTToInstructions(CompiledCodeData& output, CompileTempData ASTToInstructions(output, temp, instructions, node.children[i]); } } +} + +ScriptConstantArray ScriptCompiler::ASTParseArray(CompiledCodeData& output, CompileTempData& temp, const OptimizerModuleBase::Node& node) const { + ScriptConstantArray newConst; + for (auto& it : node.children) { + if (it.value.index() == 0) {//is code + ScriptCodePiece codeConst; + std::vector instr; + for (auto& codeIt : it.children) { + + //std::stringstream dbg; + //codeIt.dumpTree(dbg, 0); + //auto res = dbg.str(); + + instr.emplace_back(ScriptInstruction{ InstructionType::endStatement, node.offset, 0, 0 }); + ASTToInstructions(output, temp, instr, codeIt); + } + codeConst.contentString = std::get(it.value).contentString; + codeConst.code = std::move(instr); + newConst.content.emplace_back(std::move(codeConst)); + } else if (it.value.index() == 4) {//array + newConst.content.emplace_back(ASTParseArray(output, temp, it)); + } else { + newConst.content.emplace_back(it.value); + } + } + return newConst; +} + +void ScriptCompiler::ASTToInstructions(CompiledCodeData& output, CompileTempData& temp, + std::vector& instructions, const OptimizerModuleBase::Node& node) const { + + auto getFileIndex = [&](const std::string & filename) -> uint8_t + { + auto found = temp.fileLoc.find(filename); + if (found != temp.fileLoc.end()) + return found->second; + auto index = static_cast(output.fileNames.size()); + output.fileNames.emplace_back(filename); + temp.fileLoc.insert({ filename, index }); + return index; + }; + + + switch (node.type) { + case InstructionType::push: { + switch (node.value.index()) { + case 0: {//Code + ScriptConstant newConst; + std::vector instr; + for (auto& it : node.children) { + instr.emplace_back(ScriptInstruction{ InstructionType::endStatement, node.offset, 0, 0 }); + ASTToInstructions(output, temp, instr, it); + } + + newConst = node.value; + std::get(newConst).code = std::move(instr); + //#TODO duplicate detection + auto index = output.constants.size(); + output.constants.emplace_back(std::move(newConst)); + + instructions.emplace_back(ScriptInstruction{ InstructionType::push, node.offset, getFileIndex(node.file), node.line, index }); + } break; + case 1: {//String + auto index = output.constants.size(); + output.constants.emplace_back(node.value); + instructions.emplace_back(ScriptInstruction{ InstructionType::push, node.offset, getFileIndex(node.file), node.line, index }); + } break; + case 2: {//Number + auto index = output.constants.size(); + output.constants.emplace_back(node.value); + instructions.emplace_back(ScriptInstruction{ InstructionType::push, node.offset, getFileIndex(node.file), node.line, index }); + } break; + case 3: {//Bool + auto index = output.constants.size(); + output.constants.emplace_back(node.value); + instructions.emplace_back(ScriptInstruction{ InstructionType::push, node.offset, getFileIndex(node.file), node.line, index }); + } break; + case 4: {//Array + auto value = ASTParseArray(output, temp, node); + auto index = output.constants.size(); + output.constants.emplace_back(value); + + instructions.emplace_back(ScriptInstruction{ InstructionType::push, node.offset, getFileIndex(node.file), node.line, index }); + } break; + } + + }break; + case InstructionType::callUnary: { + + ASTToInstructions(output, temp, instructions, node.children[0]); + //push unary op + auto name = std::get(node.value); + std::transform(name.begin(), name.end(), name.begin(), ::tolower); + instructions.emplace_back(ScriptInstruction{ InstructionType::callUnary, node.offset, getFileIndex(node.file), node.line, name }); + + } break; + case InstructionType::callBinary: { + + + //get left arg on stack + ASTToInstructions(output, temp, instructions, node.children[0]); + //get right arg on stack + ASTToInstructions(output, temp, instructions, node.children[1]); + //push binary op + auto name = std::get(node.value); + std::transform(name.begin(), name.end(), name.begin(), ::tolower); + instructions.emplace_back(ScriptInstruction{ InstructionType::callBinary, node.offset, getFileIndex(node.file), node.line, name }); + + + + } break; + case InstructionType::callNular: { + auto name = std::get(node.value); + std::transform(name.begin(), name.end(), name.begin(), ::tolower); + instructions.emplace_back(ScriptInstruction{ InstructionType::callNular, node.offset, getFileIndex(node.file), node.line, name }); + + } break; + case InstructionType::assignTo: + case InstructionType::assignToLocal: { + + auto varname = std::get(node.value); + //need value on stack first + ASTToInstructions(output, temp, instructions, node.children[0]); + std::transform(varname.begin(), varname.end(), varname.begin(), ::tolower); + instructions.emplace_back(ScriptInstruction{ node.type, node.offset, getFileIndex(node.file), node.line, varname }); + + } break; + case InstructionType::getVariable: { + auto varname = std::get(node.value); + std::transform(varname.begin(), varname.end(), varname.begin(), ::tolower); + instructions.emplace_back(ScriptInstruction{ InstructionType::getVariable, node.offset, getFileIndex(node.file), node.line, varname }); + } break; + case InstructionType::makeArray: { + for (auto& it : node.children) + ASTToInstructions(output, temp, instructions, it); + + //#TODO can already check here if all arguments are const and push a const + + instructions.emplace_back(ScriptInstruction{ InstructionType::makeArray, node.offset, getFileIndex(node.file), node.line, node.children.size() }); + } break; + + + case InstructionType::endStatement: { + for (size_t i = 0; i < node.children.size(); i++) { + if (i != 0 || instructions.empty()) //end statement + instructions.emplace_back(ScriptInstruction{ InstructionType::endStatement, node.offset, 0, 0 }); + ASTToInstructions(output, temp, instructions, node.children[i]); + } + + } break; + } } diff --git a/src/scriptCompiler.hpp b/src/scriptCompiler.hpp index 61fcc9e..74ecbc9 100644 --- a/src/scriptCompiler.hpp +++ b/src/scriptCompiler.hpp @@ -4,6 +4,7 @@ #include #include #include "virtualmachine.h" +#include "optimizer/optimizerModuleBase.hpp" struct astnode; @@ -19,6 +20,10 @@ class ScriptCompiler { }; void ASTToInstructions(CompiledCodeData& output, CompileTempData& temp, std::vector& instructions, const astnode& node) const; + + + ScriptConstantArray ASTParseArray(CompiledCodeData& output, CompileTempData& temp, const OptimizerModuleBase::Node& node) const; + void ASTToInstructions(CompiledCodeData& output, CompileTempData& temp, std::vector& instructions, const OptimizerModuleBase::Node& node) const; void initIncludePaths(const std::vector&) const; diff --git a/src/scriptSerializer.cpp b/src/scriptSerializer.cpp index c87dd54..fa7d761 100644 --- a/src/scriptSerializer.cpp +++ b/src/scriptSerializer.cpp @@ -6,6 +6,49 @@ static constexpr const int compressionLevel = 22; +void blaBla(const CompiledCodeData& code, const std::vector& inst, std::ostream& output); + +void blaBLaConstant(const CompiledCodeData& code, const ScriptConstant& constant, std::ostream& output, bool inArray = false) { + + switch (getConstantType(constant)) { + case ConstantType::code: + output << "push CODE {\n"; + blaBla(code, std::get<0>(constant).code, output); + output << "}\n"; + break; + case ConstantType::string: + if (!inArray) + output << "push STRING " << std::get(constant) << "\n"; + else + output << std::get(constant) << ", "; + break; + case ConstantType::scalar: + if (!inArray) + output << "push SCALAR " << std::get(constant) << "\n"; + else + output << std::get(constant) << ", "; + break; + case ConstantType::boolean: + if (!inArray) + output << "push BOOL " << std::get(constant) << "\n"; + else + output << std::get(constant) << ", "; + break; + case ConstantType::array: + if (!inArray) + output << "push ARRAY [\n"; + else + output << "[\n"; + for (auto& it : std::get<4>(constant).content) + blaBLaConstant(code, it, output, true); + output << "]\n"; + break; + default:; + } + +} + + void blaBla(const CompiledCodeData& code, const std::vector& inst , std::ostream& output) {//#TODO move into proper func for (auto& it : inst) { switch (it.type) { @@ -15,24 +58,7 @@ void blaBla(const CompiledCodeData& code, const std::vector& case InstructionType::push: { auto index = std::get(it.content); auto constant = code.constants[index]; - - switch (getConstantType(constant)) { - case ConstantType::code: - output << "push CODE {\n"; - blaBla(code, std::get<0>(constant).code, output); - output << "}\n"; - break; - case ConstantType::string: - output << "push STRING " << std::get(constant) << "\n"; - break; - case ConstantType::scalar: - output << "push SCALAR " << std::get(constant) << "\n"; - break; - case ConstantType::boolean: - output << "push BOOL " << std::get(constant) << "\n"; - break; - default:; - } + blaBLaConstant(code, constant, output); } break; case InstructionType::callUnary: output << "callUnary " << std::get(it.content) << "\n";