├── .clang-format ├── .gitattributes ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── CMakeSettings.json ├── Chapter01 ├── CMakeLists.txt ├── Exercises.h ├── Exercises.inl ├── Program.cpp ├── Program.h └── Test.cpp ├── Chapter02 ├── CMakeLists.txt ├── Program.cpp ├── Program.h └── Test.cpp ├── Chapter03 ├── CMakeLists.txt ├── DeclerationParser.h ├── ErrorHandler.h ├── ExpressionParser.h ├── IdentifierParser.h ├── Program.cpp ├── Program.h ├── Skipper.h └── Test.cpp ├── Chapter04 ├── AbstractSyntaxTree.h ├── Annotation.h ├── CMakeLists.txt ├── DeclerationParser.h ├── ErrorHandler.h ├── ExpressionParser.h ├── IdentifierParser.h ├── Program.cpp ├── Program.h ├── Skipper.h ├── StringParser.h └── Test.cpp ├── Chapter05 ├── AbstractSyntaxTree.h ├── Annotation.h ├── CMakeLists.txt ├── Compiler.cpp ├── Compiler.h ├── DeclerationParser.h ├── ErrorHandler.h ├── ExpressionParser.h ├── IdentifierParser.h ├── Program.cpp ├── Program.h ├── Skipper.h ├── StringParser.h ├── Test.cpp └── Types.h ├── Chapter06 ├── AbstractSyntaxTree.h ├── Annotation.h ├── CMakeLists.txt ├── CallingConvention.h ├── Compiler.cpp ├── Compiler.h ├── DeclerationParser.h ├── ErrorHandler.h ├── EscapeAnalyser.cpp ├── EscapeAnalyser.h ├── ExpressionParser.h ├── Frame.h ├── IdentifierParser.h ├── Program.cpp ├── Program.h ├── Skipper.h ├── StringParser.h ├── TempMap.cpp ├── TempMap.h ├── Test.cpp ├── Translator.cpp ├── Translator.h ├── Types.h └── x64FastCall │ ├── CallingConvention.cpp │ ├── CallingConvention.h │ ├── Frame.cpp │ └── Frame.h ├── Chapter07 ├── AbstractSyntaxTree.h ├── Annotation.h ├── CMakeLists.txt ├── CallingConvention.h ├── DeclerationParser.h ├── ErrorHandler.h ├── EscapeAnalyser.cpp ├── EscapeAnalyser.h ├── ExpressionParser.h ├── Fragment.h ├── Frame.h ├── IdentifierParser.h ├── Program.cpp ├── Program.h ├── SemanticAnalyzer.cpp ├── SemanticAnalyzer.h ├── Skipper.h ├── StringParser.h ├── TempMap.cpp ├── TempMap.h ├── Test.cpp ├── Translator.cpp ├── Translator.h ├── Tree.cpp ├── Tree.h ├── Types.h └── x64FastCall │ ├── CallingConvention.cpp │ ├── CallingConvention.h │ ├── Frame.cpp │ ├── Frame.h │ └── Registers.h ├── Chapter08 ├── AbstractSyntaxTree.h ├── Annotation.h ├── CMakeLists.txt ├── CallingConvention.h ├── Canonicalizer.cpp ├── Canonicalizer.h ├── DeclerationParser.h ├── ErrorHandler.h ├── EscapeAnalyser.cpp ├── EscapeAnalyser.h ├── ExpressionParser.h ├── Fragment.h ├── Frame.h ├── IdentifierParser.h ├── Program.cpp ├── Program.h ├── SemanticAnalyzer.cpp ├── SemanticAnalyzer.h ├── Skipper.h ├── StringParser.h ├── TempMap.cpp ├── TempMap.h ├── Test.cpp ├── Translator.cpp ├── Translator.h ├── Tree.cpp ├── Tree.h ├── Types.h └── x64FastCall │ ├── CallingConvention.cpp │ ├── CallingConvention.h │ ├── Frame.cpp │ ├── Frame.h │ └── Registers.h ├── Chapter09 ├── AbstractSyntaxTree.h ├── Annotation.h ├── Assembly.cpp ├── Assembly.h ├── CMakeLists.txt ├── CallingConvention.cpp ├── CallingConvention.h ├── Canonicalizer.cpp ├── Canonicalizer.h ├── CodeGenerator.cpp ├── CodeGenerator.h ├── DeclerationParser.h ├── ErrorHandler.h ├── EscapeAnalyser.cpp ├── EscapeAnalyser.h ├── ExpressionParser.h ├── Fragment.h ├── Frame.h ├── IdentifierParser.h ├── Machine.h ├── MachineRegistrar.h ├── MachineRegistration.h.in ├── Program.cpp ├── Program.h ├── SemanticAnalyzer.cpp ├── SemanticAnalyzer.h ├── Skipper.h ├── StringParser.h ├── TempMap.cpp ├── TempMap.h ├── Translator.cpp ├── Translator.h ├── Tree.cpp ├── Tree.h ├── TreeAdapted.h ├── Types.h ├── m68k │ ├── CMakeLists.txt │ ├── m68kCallingConvention.cpp │ ├── m68kCallingConvention.h │ ├── m68kCodeGenerator.cpp │ ├── m68kCodeGenerator.h │ ├── m68kFrame.cpp │ ├── m68kFrame.h │ ├── m68kMachine.cpp │ ├── m68kMachine.h │ └── m68kRegisters.h ├── test │ ├── CMakeLists.txt │ ├── Test.h │ ├── TestMain.cpp │ ├── arithmetic.cpp │ ├── array.cpp │ ├── assignment.cpp │ ├── boolean.cpp │ ├── break.cpp │ ├── checkedCompile.cpp │ ├── comparison.cpp │ ├── compileFiles.cpp │ ├── for.cpp │ ├── functionCall.cpp │ ├── functionDeclarations.cpp │ ├── ifThen.cpp │ ├── integer.cpp │ ├── library.cpp │ ├── lvalue.cpp │ ├── nil.cpp │ ├── record.cpp │ ├── sequence.cpp │ ├── string.cpp │ ├── typeDeclarations.cpp │ └── while.cpp └── x64 │ ├── CMakeLists.txt │ ├── x64CallingConvention.cpp │ ├── x64CallingConvention.h │ ├── x64CodeGenerator.cpp │ ├── x64CodeGenerator.h │ ├── x64Frame.cpp │ ├── x64Frame.h │ ├── x64Machine.cpp │ ├── x64Machine.h │ └── x64Registers.h ├── Chapter10 ├── AbstractSyntaxTree.h ├── Annotation.h ├── Assembly.cpp ├── Assembly.h ├── CMakeLists.txt ├── CallingConvention.cpp ├── CallingConvention.h ├── Canonicalizer.cpp ├── Canonicalizer.h ├── CodeGenerator.cpp ├── CodeGenerator.h ├── DeclerationParser.h ├── ErrorHandler.h ├── EscapeAnalyser.cpp ├── EscapeAnalyser.h ├── ExpressionParser.h ├── FlowGraph.cpp ├── FlowGraph.h ├── Fragment.h ├── Frame.cpp ├── Frame.h ├── IdentifierParser.h ├── LivenessAnalyser.cpp ├── LivenessAnalyser.h ├── Machine.h ├── MachineRegistrar.h ├── MachineRegistration.h.in ├── Program.cpp ├── Program.h ├── SemanticAnalyzer.cpp ├── SemanticAnalyzer.h ├── Skipper.h ├── StringParser.h ├── TempLabel.h ├── TempMap.cpp ├── TempMap.h ├── TempRegister.h ├── Translator.cpp ├── Translator.h ├── Tree.cpp ├── Tree.h ├── TreeAdapted.h ├── Types.h ├── m68k │ ├── CMakeLists.txt │ ├── m68kCallingConvention.cpp │ ├── m68kCallingConvention.h │ ├── m68kCodeGenerator.cpp │ ├── m68kCodeGenerator.h │ ├── m68kFrame.cpp │ ├── m68kFrame.h │ ├── m68kMachine.cpp │ ├── m68kMachine.h │ └── m68kRegisters.h ├── test │ ├── CMakeLists.txt │ ├── Test.cpp │ ├── Test.h │ ├── TestMain.cpp │ ├── arithmetic.cpp │ ├── array.cpp │ ├── assignment.cpp │ ├── boolean.cpp │ ├── break.cpp │ ├── comparison.cpp │ ├── compileFiles.cpp │ ├── for.cpp │ ├── functionCall.cpp │ ├── functionDeclarations.cpp │ ├── ifThen.cpp │ ├── integer.cpp │ ├── library.cpp │ ├── lvalue.cpp │ ├── nil.cpp │ ├── record.cpp │ ├── sequence.cpp │ ├── string.cpp │ ├── typeDeclarations.cpp │ └── while.cpp └── x64 │ ├── CMakeLists.txt │ ├── x64CallingConvention.cpp │ ├── x64CallingConvention.h │ ├── x64CodeGenerator.cpp │ ├── x64CodeGenerator.h │ ├── x64Frame.cpp │ ├── x64Frame.h │ ├── x64Machine.cpp │ ├── x64Machine.h │ └── x64Registers.h ├── README.md ├── appveyor.yml ├── book └── Modern Compiler Implementation in C.pdf ├── cmake ├── CatchAddTests.cmake ├── CatchDiscoverTests.cmake ├── CompilerWarnings.cmake └── HunterGate.cmake ├── include ├── CMakeLists.txt ├── fusionAccumulate.h ├── irange.h ├── overload_set.h ├── printRange.h ├── satisfy_boost_range_backport.hpp ├── strong_typedef.h ├── testsHelper.h.in ├── type_traits.h ├── variantMatch.h ├── vectorApply.h └── warning_suppress.h └── module_bundle ├── chap1 ├── main.c ├── makefile ├── prog1.c ├── prog1.h ├── slp.c ├── slp.h ├── util.c └── util.h ├── chap10 ├── flowgraph.h ├── graph.c └── graph.h ├── chap11 ├── color.h └── regalloc.h ├── chap12 └── runtime.c ├── chap2 ├── driver.c ├── errormsg.c ├── errormsg.h ├── makefile ├── tiger.lex ├── tokens.h ├── util.c └── util.h ├── chap3 ├── errormsg.c ├── errormsg.h ├── lex.yy.c ├── makefile ├── parsetest.c ├── tiger.grm ├── util.c ├── util.h └── y.output ├── chap4 ├── absyn.c ├── absyn.h ├── lex.yy.c ├── makefile ├── parse.c ├── parse.h ├── prabsyn.c ├── prabsyn.h ├── symbol.c ├── symbol.h ├── table.c ├── table.h └── tiger.grm ├── chap5 ├── types.c └── types.h ├── chap6 ├── temp.c └── temp.h ├── chap7 ├── printtree.c ├── printtree.h ├── tree.c └── tree.h ├── chap8 ├── canon.c └── canon.h ├── chap9 ├── assem.c ├── assem.h ├── canon.c ├── canon.h └── main.c └── testcases ├── compilation_error ├── test10.tig ├── test11.tig ├── test13.tig ├── test14.tig ├── test15.tig ├── test16.tig ├── test17.tig ├── test18.tig ├── test19.tig ├── test20.tig ├── test21.tig ├── test22.tig ├── test23.tig ├── test24.tig ├── test25.tig ├── test26.tig ├── test28.tig ├── test29.tig ├── test31.tig ├── test32.tig ├── test33.tig ├── test34.tig ├── test35.tig ├── test36.tig ├── test40.tig ├── test43.tig ├── test45.tig └── test9.tig ├── merge.tig ├── parse_error └── test49.tig ├── queens.tig ├── test1.tig ├── test12.tig ├── test2.tig ├── test27.tig ├── test3.tig ├── test30.tig ├── test37.tig ├── test38.tig ├── test39.tig ├── test4.tig ├── test41.tig ├── test42.tig ├── test44.tig ├── test46.tig ├── test47.tig ├── test48.tig ├── test5.tig ├── test6.tig ├── test7.tig └── test8.tig /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | BasedOnStyle: LLVM 3 | AlignConsecutiveAssignments: true 4 | AllowAllParametersOfDeclarationOnNextLine: false 5 | AllowShortBlocksOnASingleLine: true 6 | AlwaysBreakTemplateDeclarations: false 7 | BraceWrapping: 8 | IndentBraces: false 9 | SplitEmptyFunction: false 10 | SplitEmptyRecord: false 11 | SplitEmptyNamespace: false 12 | BreakBeforeBinaryOperators: NonAssignment 13 | BreakBeforeBraces: Custom 14 | BreakConstructorInitializers: AfterColon 15 | BreakStringLiterals: false 16 | ColumnLimit: 80 17 | ContinuationIndentWidth: 2 18 | IncludeBlocks: Merge 19 | IndentCaseLabels: true 20 | IndentWrappedFunctionNames: true 21 | KeepEmptyLinesAtTheStartOfBlocks: false 22 | MaxEmptyLinesToKeep: 1 23 | Standard: Cpp11 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Visual Studio 2015 cache/options directory 2 | .vs/ 3 | 4 | # VS Code 5 | .vscode/ 6 | 7 | # build folders 8 | _*/ 9 | build 10 | -------------------------------------------------------------------------------- /Chapter01/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES Program.cpp Exercises.inl Test.cpp) 2 | set(HEADERS Program.h Exercises.h) 3 | 4 | add_executable(Chapter01 ${HEADERS} ${SOURCES}) 5 | 6 | target_compile_definitions(Chapter01 PRIVATE CATCH_CPP14_OR_GREATER) 7 | 8 | target_link_libraries(Chapter01 PRIVATE Boost::boost Catch2::Catch includeHeaders) 9 | 10 | parse_unit_tests(Chapter01) -------------------------------------------------------------------------------- /Chapter02/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES Program.cpp Test.cpp) 2 | set(HEADERS Program.h) 3 | 4 | add_executable(Chapter02 ${HEADERS} ${SOURCES}) 5 | 6 | target_compile_definitions(Chapter02 PRIVATE CATCH_CPP14_OR_GREATER) 7 | 8 | target_link_libraries(Chapter02 9 | PRIVATE 10 | Boost::boost 11 | Boost::filesystem 12 | Boost::system 13 | Catch2::Catch 14 | includeHeaders) 15 | 16 | parse_unit_tests(Chapter02) -------------------------------------------------------------------------------- /Chapter02/Program.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | bool lexFile(const std::string &filename); -------------------------------------------------------------------------------- /Chapter02/Test.cpp: -------------------------------------------------------------------------------- 1 | #include "Program.h" 2 | #include "testsHelper.h" 3 | #define CATCH_CONFIG_MAIN 4 | #include 5 | 6 | TEST_CASE("lex test files") { 7 | namespace fs = boost::filesystem; 8 | tiger::forEachTigerTest([](const fs::path &filepath, bool /*parseError*/, 9 | bool /*compilationError*/) { 10 | auto filename = filepath.filename(); 11 | CAPTURE(filename); 12 | REQUIRE(lexFile(filepath.string())); 13 | }); 14 | } -------------------------------------------------------------------------------- /Chapter03/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES Program.cpp Test.cpp) 2 | set(HEADERS Program.h ErrorHandler.h ExpressionParser.h Skipper.h IdentifierParser.h DeclerationParser.h) 3 | 4 | add_executable(Chapter03 ${HEADERS} ${SOURCES}) 5 | 6 | target_compile_definitions(Chapter03 PRIVATE CATCH_CPP14_OR_GREATER $<$:BOOST_SPIRIT_DEBUG>) 7 | 8 | target_link_libraries(Chapter03 9 | PRIVATE 10 | Boost::boost 11 | Boost::filesystem 12 | Boost::system 13 | Catch2::Catch 14 | includeHeaders) 15 | 16 | parse_unit_tests(Chapter03) -------------------------------------------------------------------------------- /Chapter03/ErrorHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace tiger { 7 | template class ErrorHandler { 8 | public: 9 | template struct result { typedef void type; }; 10 | 11 | template 12 | void operator()(Message const &message, What const &what, 13 | Iterator err_pos) const { 14 | const auto &pos = err_pos.get_position(); 15 | std::cerr << message << what << " at file " << pos.file << " line " 16 | << pos.line << " column " << pos.column << '\n'; 17 | std::cerr << "'"; 18 | std::copy(err_pos.get_currentline_begin(), err_pos.get_currentline_end(), 19 | std::ostream_iterator(std::cerr)); 20 | std::cerr << "'\n"; 21 | std::cerr << std::setw(pos.column) << " " 22 | << "^- here\n"; 23 | } 24 | }; 25 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter03/IdentifierParser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "ErrorHandler.h" 3 | #include "Skipper.h" 4 | #include "warning_suppress.h" 5 | MSC_DIAG_OFF(4996 4459) 6 | #include 7 | MSC_DIAG_ON() 8 | 9 | namespace tiger { 10 | template 11 | class IdentifierParser 12 | : public boost::spirit::qi::grammar> { 13 | public: 14 | IdentifierParser(ErrorHandler & /* errorHandler */) : 15 | IdentifierParser::base_type(identifier) { 16 | namespace spirit = boost::spirit; 17 | namespace qi = spirit::qi; 18 | namespace ascii = spirit::ascii; 19 | 20 | using ascii::alnum; 21 | using ascii::alpha; 22 | 23 | using qi::lexeme; 24 | using qi::raw; 25 | 26 | keywords.add("array")("if")("then")("else")("while")("for")("to")("do")( 27 | "let")("in")("end")("of")("break")("nil")("function")("var")("type"); 28 | 29 | identifier = 30 | !lexeme[keywords >> !(alnum | '_')] >> lexeme[alpha >> *(alnum | '_')]; 31 | 32 | BOOST_SPIRIT_DEBUG_NODES((identifier)) 33 | } 34 | 35 | private: 36 | template 37 | using rule = boost::spirit::qi::rule, Signature>; 38 | template 39 | using symbols = boost::spirit::qi::symbols; 40 | 41 | rule<> identifier; 42 | 43 | symbols keywords; 44 | }; 45 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter03/Program.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace tiger { 5 | 6 | bool parseFile(const std::string &filename); 7 | 8 | bool parse(const std::string &string); 9 | 10 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter03/Skipper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "warning_suppress.h" 3 | MSC_DIAG_OFF(4996 4459) 4 | #include 5 | MSC_DIAG_ON() 6 | 7 | namespace tiger { 8 | template 9 | class Skipper : public boost::spirit::qi::grammar { 10 | public: 11 | Skipper() : Skipper::base_type(start) { 12 | namespace ascii = boost::spirit::ascii; 13 | 14 | using ascii::char_; 15 | ascii::space_type space; 16 | 17 | start = space // tab/space/cr/lf 18 | | "/*" >> *(char_ - "*/") >> "*/" // comments 19 | ; 20 | 21 | BOOST_SPIRIT_DEBUG_NODES((start)) 22 | } 23 | 24 | private: 25 | boost::spirit::qi::rule start; 26 | }; 27 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter04/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES Program.cpp Test.cpp) 2 | set(HEADERS Program.h ErrorHandler.h ExpressionParser.h Skipper.h IdentifierParser.h DeclerationParser.h AbstractSyntaxTree.h Annotation.h 3 | StringParser.h) 4 | 5 | add_executable(Chapter04 ${HEADERS} ${SOURCES}) 6 | 7 | target_compile_definitions(Chapter04 PRIVATE CATCH_CPP14_OR_GREATER $<$:BOOST_SPIRIT_DEBUG>) 8 | 9 | if(MSVC) 10 | set_source_files_properties(Program.cpp PROPERTIES COMPILE_FLAGS /bigobj) 11 | endif() 12 | 13 | target_link_libraries(Chapter04 14 | PRIVATE 15 | Boost::boost 16 | Boost::filesystem 17 | Boost::system 18 | Catch2::Catch 19 | includeHeaders) 20 | 21 | parse_unit_tests(Chapter04) -------------------------------------------------------------------------------- /Chapter04/ErrorHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace tiger { 7 | template class ErrorHandler { 8 | public: 9 | template struct result { typedef void type; }; 10 | 11 | template 12 | void operator()(Message const &message, What const &what, 13 | Iterator err_pos) const { 14 | const auto &pos = err_pos.get_position(); 15 | std::cerr << message << what << " at file " << pos.file << " line " 16 | << pos.line << " column " << pos.column << '\n'; 17 | std::cerr << "'"; 18 | std::copy(err_pos.get_currentline_begin(), err_pos.get_currentline_end(), 19 | std::ostream_iterator(std::cerr)); 20 | std::cerr << "'\n"; 21 | std::cerr << std::setw(pos.column) << " " 22 | << "^- here\n"; 23 | } 24 | }; 25 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter04/Program.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "AbstractSyntaxTree.h" 3 | #include 4 | 5 | namespace tiger { 6 | 7 | bool parseFile(const std::string &filename, ast::Expression &ast); 8 | 9 | bool parse(const std::string &string, ast::Expression &ast); 10 | 11 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter04/Skipper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "warning_suppress.h" 3 | MSC_DIAG_OFF(4996 4459) 4 | #include 5 | MSC_DIAG_ON() 6 | 7 | namespace tiger { 8 | template 9 | class Skipper : public boost::spirit::qi::grammar { 10 | public: 11 | Skipper() : Skipper::base_type(skip) { 12 | namespace ascii = boost::spirit::ascii; 13 | 14 | using ascii::char_; 15 | ascii::space_type space; 16 | 17 | skip = space // tab/space/cr/lf 18 | | "/*" >> *(char_ - "*/") >> "*/" // comments 19 | ; 20 | } 21 | 22 | private: 23 | boost::spirit::qi::rule skip; 24 | }; 25 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter05/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES Program.cpp Test.cpp Compiler.cpp) 2 | set(HEADERS Program.h ErrorHandler.h ExpressionParser.h Skipper.h IdentifierParser.h DeclerationParser.h AbstractSyntaxTree.h Annotation.h 3 | StringParser.h Compiler.h Types.h) 4 | 5 | add_executable(Chapter05 ${HEADERS} ${SOURCES}) 6 | 7 | target_compile_definitions(Chapter05 PRIVATE CATCH_CPP14_OR_GREATER) 8 | 9 | if(MSVC) 10 | set_source_files_properties(Program.cpp PROPERTIES COMPILE_FLAGS /bigobj) 11 | endif() 12 | 13 | target_link_libraries(Chapter05 14 | PRIVATE 15 | Boost::boost 16 | Boost::filesystem 17 | Boost::system 18 | Catch2::Catch 19 | includeHeaders) 20 | 21 | parse_unit_tests(Chapter05) -------------------------------------------------------------------------------- /Chapter05/ErrorHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace tiger { 7 | template class ErrorHandler { 8 | public: 9 | template struct result { typedef void type; }; 10 | 11 | template 12 | void operator()(Message const &message, What const &what, 13 | Iterator err_pos) const { 14 | const auto &pos = err_pos.get_position(); 15 | std::cerr << message << ": " << what << " at file " << pos.file << " line " 16 | << pos.line << " column " << pos.column << '\n'; 17 | std::cerr << "'"; 18 | std::copy(err_pos.get_currentline_begin(), err_pos.get_currentline_end(), 19 | std::ostream_iterator(std::cerr)); 20 | std::cerr << "'\n"; 21 | std::cerr << std::setw(pos.column) << " " 22 | << "^- here\n"; 23 | } 24 | }; 25 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter05/Program.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace tiger { 5 | 6 | bool compileFile(const std::string &filename); 7 | 8 | bool compile(const std::string &string); 9 | 10 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter05/Skipper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "warning_suppress.h" 3 | MSC_DIAG_OFF(4996 4459) 4 | #include 5 | MSC_DIAG_ON() 6 | 7 | namespace tiger { 8 | template 9 | class Skipper : public boost::spirit::qi::grammar { 10 | public: 11 | Skipper() : Skipper::base_type(skip) { 12 | namespace ascii = boost::spirit::ascii; 13 | 14 | using ascii::char_; 15 | ascii::space_type space; 16 | 17 | skip = space // tab/space/cr/lf 18 | | "/*" >> *(char_ - "*/") >> "*/" // comments 19 | ; 20 | } 21 | 22 | private: 23 | boost::spirit::qi::rule skip; 24 | }; 25 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter05/Types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tiger { 9 | 10 | struct NilType {}; 11 | 12 | struct IntType {}; 13 | 14 | struct StringType {}; 15 | 16 | struct ArrayType; 17 | 18 | struct RecordType; 19 | 20 | struct NameType { 21 | std::string m_name; 22 | }; 23 | 24 | struct VoidType {}; 25 | 26 | using Type = 27 | boost::variant, 29 | boost::recursive_wrapper, NameType, VoidType>; 30 | 31 | struct NamedType { 32 | std::string m_name; 33 | Type m_type; 34 | }; 35 | 36 | struct ArrayType { 37 | NamedType m_elementType; 38 | }; 39 | 40 | struct RecordType { 41 | struct RecordField { 42 | std::string m_name; 43 | NamedType m_type; 44 | }; 45 | std::vector m_fields; 46 | }; 47 | 48 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter06/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES Program.cpp Test.cpp Compiler.cpp TempMap.cpp EscapeAnalyser.cpp Translator.cpp) 2 | set(HEADERS Program.h ErrorHandler.h ExpressionParser.h Skipper.h IdentifierParser.h DeclerationParser.h AbstractSyntaxTree.h Annotation.h 3 | StringParser.h Compiler.h Types.h TempMap.h Frame.h CallingConvention.h EscapeAnalyser.h Translator.h) 4 | list(APPEND SOURCES x64FastCall/Frame.cpp x64FastCall/CallingConvention.cpp) 5 | list(APPEND HEADERS x64FastCall/Frame.h x64FastCall/Frame.cpp) 6 | 7 | add_executable(Chapter06 ${HEADERS} ${SOURCES}) 8 | 9 | target_compile_definitions(Chapter06 PRIVATE CATCH_CPP14_OR_GREATER) 10 | 11 | if(MSVC) 12 | set_source_files_properties(Program.cpp PROPERTIES COMPILE_FLAGS /bigobj) 13 | endif() 14 | 15 | target_link_libraries(Chapter06 16 | PRIVATE 17 | Boost::boost 18 | Boost::filesystem 19 | Boost::system 20 | Catch2::Catch 21 | includeHeaders) 22 | 23 | parse_unit_tests(Chapter06) -------------------------------------------------------------------------------- /Chapter06/CallingConvention.h: -------------------------------------------------------------------------------- 1 | #include "Frame.h" 2 | 3 | namespace tiger { 4 | class CallingConvention { 5 | public: 6 | virtual std::unique_ptr createFrame(TempMap &tempMap, 7 | const Label &name, 8 | const BoolList &formals) = 0; 9 | }; 10 | } // namespace tiger 11 | -------------------------------------------------------------------------------- /Chapter06/ErrorHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace tiger { 7 | template class ErrorHandler { 8 | public: 9 | template struct result { typedef void type; }; 10 | 11 | template 12 | void operator()(Message const &message, What const &what, 13 | Iterator err_pos) const { 14 | const auto &pos = err_pos.get_position(); 15 | std::cerr << message << ": " << what << " at file " << pos.file << " line " 16 | << pos.line << " column " << pos.column << '\n'; 17 | std::cerr << "'"; 18 | std::copy(err_pos.get_currentline_begin(), err_pos.get_currentline_end(), 19 | std::ostream_iterator(std::cerr)); 20 | std::cerr << "'\n"; 21 | std::cerr << std::setw(pos.column) << " " 22 | << "^- here\n"; 23 | } 24 | }; 25 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter06/EscapeAnalyser.h: -------------------------------------------------------------------------------- 1 | #include "AbstractSyntaxTree.h" 2 | #include 3 | #include 4 | 5 | namespace tiger { 6 | class EscapeAnalyser { 7 | public: 8 | using result_type = bool; 9 | 10 | result_type analyse(ast::Expression &exp); 11 | 12 | private: 13 | result_type analyseVar(ast::VarExpression &exp); 14 | result_type analyseLet(ast::LetExpression &exp); 15 | result_type analyseFuncDec(ast::FunctionDeclarations &decs); 16 | result_type analyseVarDec(ast::VarDeclaration &dec); 17 | 18 | using Environment = 19 | std::unordered_map>; 20 | 21 | std::vector m_environments; 22 | }; 23 | } // namespace tiger 24 | -------------------------------------------------------------------------------- /Chapter06/Frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TempMap.h" 3 | #include 4 | #include 5 | #include 6 | 7 | namespace tiger { 8 | 9 | using BoolList = boost::dynamic_bitset<>; 10 | 11 | struct InFrame { 12 | int m_offset; 13 | }; 14 | 15 | struct InReg { 16 | Temporary m_reg; 17 | }; 18 | 19 | using VariableAccess = boost::variant; 20 | using AccessList = std::vector; 21 | 22 | class Frame { 23 | public: 24 | virtual ~Frame() noexcept = default; 25 | virtual Label name() const = 0; 26 | virtual AccessList formals() const = 0; 27 | virtual VariableAccess allocateLocal(bool escapes) = 0; 28 | }; 29 | 30 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter06/Program.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace tiger { 5 | 6 | bool compileFile(const std::string &filename); 7 | 8 | bool compile(const std::string &string); 9 | 10 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter06/Skipper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "warning_suppress.h" 3 | MSC_DIAG_OFF(4996 4459) 4 | #include 5 | MSC_DIAG_ON() 6 | 7 | namespace tiger { 8 | template 9 | class Skipper : public boost::spirit::qi::grammar { 10 | public: 11 | Skipper() : Skipper::base_type(skip) { 12 | namespace ascii = boost::spirit::ascii; 13 | 14 | using ascii::char_; 15 | ascii::space_type space; 16 | 17 | skip = space // tab/space/cr/lf 18 | | "/*" >> *(char_ - "*/") >> "*/" // comments 19 | ; 20 | } 21 | 22 | private: 23 | boost::spirit::qi::rule skip; 24 | }; 25 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter06/TempMap.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * temp.cpp - functions to create and manipulate temporary variables which are 3 | * used in the IR tree representation before it has been determined 4 | * which variables are to go into registers. 5 | * 6 | */ 7 | 8 | #include "TempMap.h" 9 | #include 10 | #include 11 | 12 | namespace tiger { 13 | 14 | std::ostream &operator<<(std::ostream &ost, const TempMap &map) { 15 | for (const auto &it : map.m_map) { 16 | ost << "t" << it.first << " -> " << it.second << '\n'; 17 | } 18 | if (map.m_under) { 19 | ost << "---------\n"; 20 | ost << *map.m_under; 21 | } 22 | 23 | return ost; 24 | } 25 | 26 | void TempMap::layerOver(TempMap &&under) { 27 | if (m_under) { 28 | m_under->layerOver(std::move(under)); 29 | } else { 30 | m_under.reset(&under); 31 | } 32 | } 33 | 34 | Temporary TempMap::newTemp() { 35 | static const auto MIN_TEMP = 100; 36 | static auto temps = MIN_TEMP; 37 | auto res = temps++; 38 | m_map[res] = std::to_string(res); 39 | return res; 40 | } 41 | 42 | boost::optional TempMap::lookup(const Temporary &t) { 43 | auto it = m_map.find(t); 44 | if (it != m_map.end()) 45 | return it->second; 46 | if (m_under) 47 | return m_under->lookup(t); 48 | return {}; 49 | } 50 | 51 | Label TempMap::newLabel() { 52 | static const auto MIN_LABEL = 0; 53 | static auto labels = MIN_LABEL; 54 | return "L" + std::to_string(labels++); 55 | } 56 | 57 | } // namespace tiger 58 | -------------------------------------------------------------------------------- /Chapter06/TempMap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace tiger { 8 | 9 | // a temporary is just a number 10 | using Temporary = int; 11 | 12 | // a label is just a string 13 | using Label = std::string; 14 | 15 | // A Map is just a table whose keys are Temp_temps and whose bindings 16 | // are strings. However, one mapping can be layered over another; if σ3 = 17 | // layer(σ1, σ2), this means that look(σ3, t) will first try look(σ1, t), and if 18 | // that fails it will try look(σ2, t).Also, enter(σ3, t, a) will have the effect 19 | // of entering t->a into σ2. 20 | class TempMap { 21 | public: 22 | void layerOver(TempMap &&under); 23 | Temporary newTemp(); 24 | boost::optional lookup(const Temporary &t); 25 | friend std::ostream &operator<<(std::ostream &ost, const TempMap &map); 26 | 27 | Label newLabel(); 28 | 29 | private: 30 | std::unordered_map m_map; 31 | std::unique_ptr m_under; 32 | }; 33 | 34 | } // namespace tiger 35 | -------------------------------------------------------------------------------- /Chapter06/Translator.cpp: -------------------------------------------------------------------------------- 1 | #include "Translator.h" 2 | #include "CallingConvention.h" 3 | #include 4 | #include 5 | 6 | namespace tiger { 7 | 8 | Translator::Translator(TempMap &tempMap, CallingConvention &callingConvention) : 9 | m_tempMap(tempMap), m_callingConvention(callingConvention) { 10 | m_frames.push_back( 11 | m_callingConvention.createFrame(m_tempMap, "start", BoolList{})); 12 | } 13 | 14 | tiger::Translator::Level Translator::outermost() const { return 0; } 15 | 16 | tiger::Translator::Level Translator::newLevel(Level /* parent */, Label label, 17 | const BoolList &formals) { 18 | // add static link 19 | BoolList withStaticLink = formals; 20 | withStaticLink.resize(withStaticLink.size() + 1); 21 | (withStaticLink <<= 1).set(0, true); 22 | m_frames.push_back( 23 | m_callingConvention.createFrame(m_tempMap, label, withStaticLink)); 24 | return m_frames.size() - 1; 25 | } 26 | 27 | tiger::AccessList Translator::formals(Level level) { 28 | assert(level < m_frames.size()); 29 | return m_frames[level]->formals(); 30 | } 31 | 32 | std::pair 33 | Translator::allocateLocal(Level level, bool escapes) { 34 | assert(level < m_frames.size()); 35 | return {level, m_frames[level]->allocateLocal(escapes)}; 36 | } 37 | 38 | } // namespace tiger 39 | -------------------------------------------------------------------------------- /Chapter06/Translator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Frame.h" 3 | 4 | namespace tiger { 5 | 6 | class CallingConvention; 7 | 8 | class Translator { 9 | public: 10 | using Level = size_t; 11 | 12 | Translator(TempMap &tempMap, CallingConvention &callingConvention); 13 | 14 | Level outermost() const; 15 | Level newLevel(Level parent, Label label, const BoolList &formals); 16 | AccessList formals(Level level); 17 | std::pair allocateLocal(Level level, bool escapes); 18 | 19 | private: 20 | TempMap &m_tempMap; 21 | CallingConvention &m_callingConvention; 22 | std::vector> m_frames; 23 | }; 24 | } // namespace tiger 25 | -------------------------------------------------------------------------------- /Chapter06/Types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tiger { 9 | 10 | struct NilType {}; 11 | 12 | struct IntType {}; 13 | 14 | struct StringType {}; 15 | 16 | struct ArrayType; 17 | 18 | struct RecordType; 19 | 20 | struct NameType { 21 | std::string m_name; 22 | }; 23 | 24 | struct VoidType {}; 25 | 26 | using Type = 27 | boost::variant, 29 | boost::recursive_wrapper, NameType, VoidType>; 30 | 31 | struct NamedType { 32 | std::string m_name; 33 | Type m_type; 34 | }; 35 | 36 | struct ArrayType { 37 | NamedType m_elementType; 38 | }; 39 | 40 | struct RecordType { 41 | struct RecordField { 42 | std::string m_name; 43 | NamedType m_type; 44 | }; 45 | std::vector m_fields; 46 | }; 47 | 48 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter06/x64FastCall/CallingConvention.cpp: -------------------------------------------------------------------------------- 1 | #include "CallingConvention.h" 2 | #include "Frame.h" 3 | 4 | namespace tiger { 5 | 6 | std::unique_ptr 7 | x64FastCallCallingConvention::createFrame(TempMap &tempMap, const Label &name, 8 | const BoolList &formals) { 9 | return std::make_unique(tempMap, name, formals); 10 | } 11 | 12 | } // namespace tiger 13 | -------------------------------------------------------------------------------- /Chapter06/x64FastCall/CallingConvention.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../CallingConvention.h" 4 | 5 | namespace tiger { 6 | class x64FastCallCallingConvention : public CallingConvention { 7 | public: 8 | // Inherited via CallingConvention 9 | virtual std::unique_ptr createFrame(TempMap &tempMap, 10 | const Label &name, 11 | const BoolList &formals) override; 12 | }; 13 | } // namespace tiger 14 | -------------------------------------------------------------------------------- /Chapter06/x64FastCall/Frame.cpp: -------------------------------------------------------------------------------- 1 | #include "Frame.h" 2 | #include 3 | #include 4 | 5 | namespace tiger { 6 | namespace x64FastCall { 7 | 8 | Frame::Frame(TempMap &tempMap, const Label &name, const BoolList &formals) : 9 | m_tempMap(tempMap), m_name(name) { 10 | for (size_t i = 0; i < formals.size(); ++i) { 11 | m_formals.push_back(allocateLocal(formals[i])); 12 | } 13 | } 14 | 15 | Label Frame::name() const { return m_name; } 16 | 17 | AccessList Frame::formals() const { return m_formals; } 18 | 19 | VariableAccess Frame::allocateLocal(bool escapes) { 20 | if (escapes || m_allocatedRegs == MAX_REGS) { 21 | auto res = InFrame{m_frameOffset}; 22 | m_frameOffset += FRAME_INC; 23 | return res; 24 | } 25 | 26 | assert(m_allocatedRegs < MAX_REGS); 27 | ++m_allocatedRegs; 28 | return InReg{m_tempMap.newTemp()}; 29 | } 30 | 31 | } // namespace x64FastCall 32 | } // namespace tiger 33 | -------------------------------------------------------------------------------- /Chapter06/x64FastCall/Frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../Frame.h" 4 | 5 | namespace tiger { 6 | namespace x64FastCall { 7 | // implements 8 | // https://docs.microsoft.com/en-gb/cpp/build/overview-of-x64-calling-conventions 9 | class Frame : public tiger::Frame { 10 | public: 11 | Frame(TempMap &tempMap, const Label &name, const BoolList &formals); 12 | // Inherited via Frame 13 | virtual Label name() const override; 14 | virtual AccessList formals() const override; 15 | virtual VariableAccess allocateLocal(bool escapes) override; 16 | 17 | private: 18 | TempMap &m_tempMap; 19 | Label m_name; 20 | AccessList m_formals; 21 | static const int FRAME_INC = 4; 22 | int m_frameOffset = -FRAME_INC; 23 | // The __fastcall convention uses registers for the first four arguments 24 | static const size_t MAX_REGS = 4; 25 | size_t m_allocatedRegs = 0; 26 | }; 27 | } // namespace x64FastCall 28 | } // namespace tiger 29 | -------------------------------------------------------------------------------- /Chapter07/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES Program.cpp Test.cpp SemanticAnalyzer.cpp TempMap.cpp EscapeAnalyser.cpp Translator.cpp Tree.cpp) 2 | set(HEADERS Program.h ErrorHandler.h ExpressionParser.h Skipper.h IdentifierParser.h DeclerationParser.h AbstractSyntaxTree.h 3 | Annotation.h StringParser.h SemanticAnalyzer.h Types.h TempMap.h Frame.h CallingConvention.h EscapeAnalyser.h Translator.h Tree.h 4 | Fragment.h) 5 | list(APPEND SOURCES x64FastCall/Frame.cpp x64FastCall/CallingConvention.cpp) 6 | list(APPEND HEADERS x64FastCall/Frame.h x64FastCall/Frame.cpp) 7 | 8 | add_executable(Chapter07 ${HEADERS} ${SOURCES}) 9 | 10 | target_compile_definitions(Chapter07 PRIVATE CATCH_CPP14_OR_GREATER) 11 | 12 | if(MSVC) 13 | set_source_files_properties(Program.cpp PROPERTIES COMPILE_FLAGS /bigobj) 14 | endif() 15 | 16 | target_link_libraries(Chapter07 17 | PRIVATE 18 | Boost::boost 19 | Boost::filesystem 20 | Boost::system 21 | Catch2::Catch 22 | includeHeaders 23 | ) 24 | 25 | parse_unit_tests(Chapter07 --timeout 10) 26 | -------------------------------------------------------------------------------- /Chapter07/CallingConvention.h: -------------------------------------------------------------------------------- 1 | #include "Frame.h" 2 | 3 | namespace tiger { 4 | namespace frame { 5 | class CallingConvention { 6 | public: 7 | CallingConvention(temp::Map &tempMap) : m_tempMap(tempMap) {} 8 | 9 | virtual ~CallingConvention() = default; 10 | 11 | virtual std::unique_ptr createFrame(const temp::Label &name, 12 | const BoolList &formals) = 0; 13 | virtual int wordSize() const = 0; 14 | 15 | virtual temp::Register framePointer() const = 0; 16 | 17 | virtual temp::Register returnValue() const = 0; 18 | 19 | virtual ir::Expression accessFrame(const VariableAccess &access, 20 | const ir::Expression &framePtr) const = 0; 21 | 22 | virtual void allocateString(temp::Label label, const std::string &str) = 0; 23 | 24 | virtual ir::Expression 25 | externalCall(const std::string &name, 26 | const std::vector &args) = 0; 27 | 28 | protected: 29 | temp::Map &m_tempMap; 30 | }; 31 | } // namespace frame 32 | } // namespace tiger 33 | -------------------------------------------------------------------------------- /Chapter07/ErrorHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace tiger { 8 | template class ErrorHandler { 9 | public: 10 | template struct result { typedef void type; }; 11 | 12 | template 13 | void operator()(Message const &message, What const &what, 14 | Iterator err_pos) const { 15 | class TigerError : public std::logic_error { 16 | using std::logic_error::logic_error; 17 | }; 18 | const auto &pos = err_pos.get_position(); 19 | std::stringstream sst; 20 | sst << pos.file << "(" << pos.line << "," << pos.column << "): " << message 21 | << ": " << what << '\n'; 22 | sst << "'"; 23 | std::copy(err_pos.get_currentline_begin(), err_pos.get_currentline_end(), 24 | std::ostream_iterator(sst)); 25 | sst << "'\n"; 26 | sst << std::setw(pos.column) << " " 27 | << "^- here\n"; 28 | throw TigerError{sst.str()}; 29 | } 30 | }; 31 | } // namespace tiger 32 | -------------------------------------------------------------------------------- /Chapter07/Fragment.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Tree.h" 3 | #include 4 | 5 | namespace tiger { 6 | 7 | namespace frame { 8 | class Frame; 9 | } 10 | 11 | struct StringFragment { 12 | temp::Label m_label; 13 | std::string m_string; 14 | }; 15 | 16 | struct FunctionFragment { 17 | ir::Statement m_body; 18 | std::shared_ptr m_frame; 19 | }; 20 | 21 | using Fragment = boost::variant; 22 | using FragmentList = std::vector; 23 | } // namespace tiger 24 | -------------------------------------------------------------------------------- /Chapter07/Frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TempMap.h" 3 | #include "Tree.h" 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tiger { 9 | 10 | namespace frame { 11 | 12 | using BoolList = boost::dynamic_bitset<>; 13 | 14 | struct InFrame { 15 | int m_offset; 16 | }; 17 | 18 | struct InReg { 19 | temp::Register m_reg; 20 | }; 21 | 22 | using VariableAccess = boost::variant; 23 | using AccessList = std::vector; 24 | 25 | class Frame { 26 | public: 27 | Frame(temp::Map &tempMap) : m_tempMap(tempMap) {} 28 | 29 | virtual ~Frame() noexcept = default; 30 | 31 | virtual temp::Label name() const = 0; 32 | virtual const AccessList &formals() const = 0; 33 | virtual VariableAccess allocateLocal(bool escapes) = 0; 34 | virtual ir::Statement procEntryExit1(const ir::Statement &body) = 0; 35 | 36 | protected: 37 | temp::Map &m_tempMap; 38 | }; 39 | 40 | } // namespace frame 41 | } // namespace tiger 42 | -------------------------------------------------------------------------------- /Chapter07/Program.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Fragment.h" 3 | #include "Tree.h" 4 | #include 5 | #include 6 | 7 | namespace tiger { 8 | 9 | using CompileResult = boost::optional>; 10 | 11 | CompileResult compileFile(const std::string &filename); 12 | 13 | CompileResult compile(const std::string &string); 14 | 15 | std::ostream & 16 | operator<<(std::ostream &ost, 17 | std::pair const &compileResult); 18 | 19 | } // namespace tiger 20 | -------------------------------------------------------------------------------- /Chapter07/Skipper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "warning_suppress.h" 3 | MSC_DIAG_OFF(4996 4459) 4 | #include 5 | MSC_DIAG_ON() 6 | 7 | namespace tiger { 8 | template 9 | class Skipper : public boost::spirit::qi::grammar { 10 | public: 11 | Skipper() : Skipper::base_type(skip) { 12 | namespace ascii = boost::spirit::ascii; 13 | 14 | using ascii::char_; 15 | ascii::space_type space; 16 | 17 | skip = space // tab/space/cr/lf 18 | | "/*" >> *(char_ - "*/") >> "*/" // comments 19 | ; 20 | } 21 | 22 | private: 23 | boost::spirit::qi::rule skip; 24 | }; 25 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter07/TempMap.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * temp.cpp - functions to create and manipulate Register variables which are 3 | * used in the IR tree representation before it has been determined 4 | * which variables are to go into registers. 5 | * 6 | */ 7 | 8 | #include "TempMap.h" 9 | #include 10 | #include 11 | 12 | namespace tiger { 13 | 14 | namespace temp { 15 | 16 | std::ostream &operator<<(std::ostream &ost, const Map &map) { 17 | for (const auto &it : map.m_map) { 18 | ost << "t" << it.first << " -> " << it.second << '\n'; 19 | } 20 | if (map.m_under) { 21 | ost << "---------\n"; 22 | ost << *map.m_under; 23 | } 24 | 25 | return ost; 26 | } 27 | 28 | void Map::layerOver(Map &&under) { 29 | if (m_under) { 30 | m_under->layerOver(std::move(under)); 31 | } else { 32 | m_under.reset(&under); 33 | } 34 | } 35 | 36 | Register Map::newTemp() { 37 | Register res{m_nextTemp}; 38 | m_map[res] = std::to_string(m_nextTemp++); 39 | return res; 40 | } 41 | 42 | boost::optional Map::lookup(const Register &t) { 43 | auto it = m_map.find(t); 44 | if (it != m_map.end()) 45 | return it->second; 46 | if (m_under) 47 | return m_under->lookup(t); 48 | return {}; 49 | } 50 | 51 | Label Map::newLabel() { 52 | static const auto MIN_LABEL = 0; 53 | static auto labels = MIN_LABEL; 54 | return Label{"L" + std::to_string(labels++)}; 55 | } 56 | 57 | Label Map::namedLabel(const std::string &name) { return Label{name}; } 58 | 59 | } // namespace temp 60 | } // namespace tiger 61 | -------------------------------------------------------------------------------- /Chapter07/TempMap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "strong_typedef.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace tiger { 10 | 11 | namespace temp { 12 | 13 | // a register is just a number 14 | using Register = helpers::strong_typedef; 15 | 16 | // a label is just a string 17 | using Label = helpers::strong_typedef; 18 | 19 | constexpr auto MIN_TEMP = 100; 20 | 21 | // A Map is just a table whose keys are Temp_temps and whose bindings 22 | // are strings. However, one mapping can be layered over another; if σ3 = 23 | // layer(σ1, σ2), this means that look(σ3, t) will first try look(σ1, t), and if 24 | // that fails it will try look(σ2, t).Also, enter(σ3, t, a) will have the effect 25 | // of entering t->a into σ2. 26 | class Map { 27 | public: 28 | void layerOver(Map &&under); 29 | Register newTemp(); 30 | boost::optional lookup(const Register &t); 31 | friend std::ostream &operator<<(std::ostream &ost, const Map &map); 32 | 33 | Label newLabel(); 34 | Label namedLabel(const std::string &name); 35 | 36 | private: 37 | std::unordered_map m_map; 38 | std::unique_ptr m_under; 39 | int m_nextTemp = MIN_TEMP; 40 | }; 41 | 42 | } // namespace temp 43 | } // namespace tiger 44 | -------------------------------------------------------------------------------- /Chapter07/Types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tiger { 9 | 10 | struct NilType {}; 11 | 12 | struct IntType {}; 13 | 14 | struct StringType {}; 15 | 16 | struct ArrayType; 17 | 18 | struct RecordType; 19 | 20 | struct NameType { 21 | std::string m_name; 22 | }; 23 | 24 | struct VoidType {}; 25 | 26 | using Type = 27 | boost::variant, 29 | boost::recursive_wrapper, NameType, VoidType>; 30 | 31 | struct NamedType { 32 | std::string m_name; 33 | Type m_type; 34 | }; 35 | 36 | struct ArrayType { 37 | NamedType m_elementType; 38 | }; 39 | 40 | struct RecordType { 41 | struct RecordField { 42 | std::string m_name; 43 | NamedType m_type; 44 | }; 45 | std::vector m_fields; 46 | }; 47 | 48 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter07/x64FastCall/CallingConvention.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../CallingConvention.h" 4 | 5 | namespace tiger { 6 | namespace frame { 7 | namespace x64FastCall { 8 | class CallingConvention final : public frame::CallingConvention { 9 | public: 10 | CallingConvention(temp::Map &tempMap); 11 | 12 | virtual std::unique_ptr 13 | createFrame(const temp::Label &name, const BoolList &formals) override; 14 | 15 | virtual int wordSize() const override; 16 | 17 | virtual temp::Register framePointer() const override; 18 | 19 | virtual temp::Register returnValue() const override; 20 | 21 | virtual ir::Expression 22 | accessFrame(const VariableAccess &access, 23 | const ir::Expression &framePtr) const override; 24 | 25 | virtual void allocateString(temp::Label label, 26 | const std::string &str) override; 27 | 28 | virtual ir::Expression 29 | externalCall(const std::string &name, 30 | const std::vector &args) override; 31 | }; 32 | } // namespace x64FastCall 33 | } // namespace frame 34 | } // namespace tiger 35 | -------------------------------------------------------------------------------- /Chapter07/x64FastCall/Frame.cpp: -------------------------------------------------------------------------------- 1 | #include "Frame.h" 2 | #include "variantMatch.h" 3 | #include 4 | #include 5 | 6 | namespace tiger { 7 | namespace frame { 8 | namespace x64FastCall { 9 | 10 | std::array Frame::m_regParams = { 11 | Registers::RCX, Registers::RDX, Registers::R8, Registers::R9}; 12 | 13 | Frame::Frame(temp::Map &tempMap, const temp::Label &name, 14 | const BoolList &formals) : 15 | frame::Frame(tempMap), 16 | m_name(name) { 17 | for (size_t i = 0; i < formals.size(); ++i) { 18 | auto varAccess = [this](bool escapes) -> VariableAccess { 19 | if (escapes || m_allocatedRegs == MAX_REGS) { 20 | auto res = InFrame{m_frameOffset}; 21 | m_frameOffset -= WORD_SIZE; 22 | return res; 23 | } 24 | 25 | assert(m_allocatedRegs < MAX_REGS); 26 | return InReg{reg(m_regParams[m_allocatedRegs++])}; 27 | }(formals[i]); 28 | m_formals.push_back(varAccess); 29 | } 30 | } 31 | 32 | temp::Label Frame::name() const { return m_name; } 33 | 34 | const AccessList &Frame::formals() const { return m_formals; } 35 | 36 | VariableAccess Frame::allocateLocal(bool escapes) { 37 | if (escapes) { 38 | auto res = InFrame{m_frameOffset}; 39 | m_frameOffset -= WORD_SIZE; 40 | return res; 41 | } 42 | 43 | return InReg{m_tempMap.newTemp()}; 44 | } 45 | 46 | ir::Statement Frame::procEntryExit1(const ir::Statement &body) { return body; } 47 | 48 | } // namespace x64FastCall 49 | } // namespace frame 50 | } // namespace tiger 51 | -------------------------------------------------------------------------------- /Chapter07/x64FastCall/Frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../Frame.h" 4 | #include "Registers.h" 5 | #include 6 | 7 | namespace tiger { 8 | namespace frame { 9 | namespace x64FastCall { 10 | // implements 11 | // https://docs.microsoft.com/en-gb/cpp/build/overview-of-x64-calling-conventions 12 | class Frame final : public frame::Frame { 13 | public: 14 | static const int WORD_SIZE = 8; 15 | 16 | Frame(temp::Map &tempMap, const temp::Label &name, const BoolList &formals); 17 | 18 | // Inherited via Frame 19 | virtual temp::Label name() const override; 20 | 21 | virtual const AccessList &formals() const override; 22 | 23 | virtual VariableAccess allocateLocal(bool escapes) override; 24 | 25 | virtual ir::Statement procEntryExit1(const ir::Statement &body) override; 26 | 27 | private: 28 | temp::Label m_name; 29 | AccessList m_formals; 30 | int m_frameOffset = -WORD_SIZE; 31 | // The __fastcall convention uses registers for the first four arguments 32 | static const size_t MAX_REGS = 4; 33 | static std::array m_regParams; 34 | size_t m_allocatedRegs = 0; 35 | }; 36 | } // namespace x64FastCall 37 | } // namespace frame 38 | } // namespace tiger 39 | -------------------------------------------------------------------------------- /Chapter07/x64FastCall/Registers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../TempMap.h" 3 | 4 | namespace tiger { 5 | namespace frame { 6 | namespace x64FastCall { 7 | 8 | enum class Registers { 9 | RAX, 10 | RDX, 11 | RCX, 12 | RBX, 13 | RSI, 14 | RDI, 15 | RBP, 16 | RSP, 17 | R8, 18 | R9, 19 | R10, 20 | R11, 21 | R12, 22 | R13, 23 | R14, 24 | R15, 25 | RIP, 26 | MM0, 27 | MM1, 28 | MM2, 29 | MM3, 30 | MM4, 31 | MM5, 32 | MM6, 33 | MM7, 34 | FP0, 35 | FP1, 36 | FP2, 37 | FP3, 38 | FP4, 39 | FP5, 40 | FP6, 41 | FP7, 42 | XMM8, 43 | XMM9, 44 | XMM10, 45 | XMM11, 46 | XMM12, 47 | XMM13, 48 | XMM14, 49 | XMM15, 50 | XMM16, 51 | XMM17, 52 | XMM18, 53 | XMM19, 54 | XMM20, 55 | XMM21, 56 | XMM22, 57 | XMM23, 58 | XMM24, 59 | XMM25, 60 | XMM26, 61 | XMM27, 62 | XMM28, 63 | XMM29, 64 | XMM30, 65 | XMM31, 66 | Size 67 | }; 68 | 69 | constexpr temp::Register reg(Registers reg) { 70 | return temp::Register{static_cast(reg)}; 71 | } 72 | 73 | static_assert(static_cast(Registers::Size) <= temp::MIN_TEMP, 74 | "MIN_TEMP should be greater than the greatest register"); 75 | } // namespace x64FastCall 76 | } // namespace frame 77 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter08/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES Program.cpp Test.cpp SemanticAnalyzer.cpp TempMap.cpp EscapeAnalyser.cpp Translator.cpp Tree.cpp Canonicalizer.cpp) 2 | set(HEADERS Program.h ErrorHandler.h ExpressionParser.h Skipper.h IdentifierParser.h DeclerationParser.h AbstractSyntaxTree.h 3 | Annotation.h StringParser.h SemanticAnalyzer.h Types.h TempMap.h Frame.h CallingConvention.h EscapeAnalyser.h Translator.h Tree.h 4 | Fragment.h Canonicalizer.h) 5 | list(APPEND SOURCES x64FastCall/Frame.cpp x64FastCall/CallingConvention.cpp) 6 | list(APPEND HEADERS x64FastCall/Frame.h x64FastCall/Frame.cpp) 7 | 8 | add_executable(Chapter08 ${HEADERS} ${SOURCES}) 9 | 10 | target_compile_definitions(Chapter08 PRIVATE CATCH_CPP14_OR_GREATER) 11 | 12 | if(MSVC) 13 | set_source_files_properties(Program.cpp PROPERTIES COMPILE_FLAGS /bigobj) 14 | elseif(CMAKE_CXX_COMPILER_ID STREQUAL Clang) 15 | # https://stackoverflow.com/a/23376156/621176 16 | set_source_files_properties(Test.cpp PROPERTIES COMPILE_FLAGS -ftemplate-depth=1024) 17 | endif() 18 | 19 | target_link_libraries(Chapter08 20 | PRIVATE 21 | Boost::boost 22 | Boost::filesystem 23 | Boost::system 24 | Catch2::Catch 25 | includeHeaders 26 | ) 27 | 28 | parse_unit_tests(Chapter08 --timeout 10) 29 | -------------------------------------------------------------------------------- /Chapter08/CallingConvention.h: -------------------------------------------------------------------------------- 1 | #include "Frame.h" 2 | 3 | namespace tiger { 4 | namespace frame { 5 | class CallingConvention { 6 | public: 7 | CallingConvention(temp::Map &tempMap) : m_tempMap(tempMap) {} 8 | 9 | virtual ~CallingConvention() = default; 10 | 11 | virtual std::unique_ptr createFrame(const temp::Label &name, 12 | const BoolList &formals) = 0; 13 | virtual int wordSize() const = 0; 14 | 15 | virtual temp::Register framePointer() const = 0; 16 | 17 | virtual temp::Register returnValue() const = 0; 18 | 19 | virtual ir::Expression accessFrame(const VariableAccess &access, 20 | const ir::Expression &framePtr) const = 0; 21 | 22 | virtual void allocateString(temp::Label label, const std::string &str) = 0; 23 | 24 | virtual ir::Expression 25 | externalCall(const std::string &name, 26 | const std::vector &args) = 0; 27 | 28 | protected: 29 | temp::Map &m_tempMap; 30 | }; 31 | } // namespace frame 32 | } // namespace tiger 33 | -------------------------------------------------------------------------------- /Chapter08/ErrorHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace tiger { 8 | template class ErrorHandler { 9 | public: 10 | template struct result { typedef void type; }; 11 | 12 | template 13 | void operator()(Message const &message, What const &what, 14 | Iterator err_pos) const { 15 | class TigerError : public std::logic_error { 16 | using std::logic_error::logic_error; 17 | }; 18 | const auto &pos = err_pos.get_position(); 19 | std::stringstream sst; 20 | sst << pos.file << "(" << pos.line << "," << pos.column << "): " << message 21 | << ": " << what << '\n'; 22 | sst << "'"; 23 | std::copy(err_pos.get_currentline_begin(), err_pos.get_currentline_end(), 24 | std::ostream_iterator(sst)); 25 | sst << "'\n"; 26 | sst << std::setw(pos.column) << " " 27 | << "^- here\n"; 28 | throw TigerError{sst.str()}; 29 | } 30 | }; 31 | } // namespace tiger 32 | -------------------------------------------------------------------------------- /Chapter08/Fragment.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Tree.h" 3 | #include 4 | 5 | namespace tiger { 6 | 7 | namespace frame { 8 | class Frame; 9 | } 10 | 11 | struct StringFragment { 12 | temp::Label m_label; 13 | std::string m_string; 14 | }; 15 | 16 | struct FunctionFragment { 17 | ir::Statement m_body; 18 | std::shared_ptr m_frame; 19 | }; 20 | 21 | using Fragment = boost::variant; 22 | using FragmentList = std::vector; 23 | } // namespace tiger 24 | -------------------------------------------------------------------------------- /Chapter08/Frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TempMap.h" 3 | #include "Tree.h" 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tiger { 9 | 10 | namespace frame { 11 | 12 | using BoolList = boost::dynamic_bitset<>; 13 | 14 | struct InFrame { 15 | int m_offset; 16 | }; 17 | 18 | struct InReg { 19 | temp::Register m_reg; 20 | }; 21 | 22 | using VariableAccess = boost::variant; 23 | using AccessList = std::vector; 24 | 25 | class Frame { 26 | public: 27 | Frame(temp::Map &tempMap) : m_tempMap(tempMap) {} 28 | 29 | virtual ~Frame() noexcept = default; 30 | 31 | virtual temp::Label name() const = 0; 32 | virtual const AccessList &formals() const = 0; 33 | virtual VariableAccess allocateLocal(bool escapes) = 0; 34 | virtual ir::Statement procEntryExit1(const ir::Statement &body) = 0; 35 | 36 | protected: 37 | temp::Map &m_tempMap; 38 | }; 39 | 40 | } // namespace frame 41 | } // namespace tiger 42 | -------------------------------------------------------------------------------- /Chapter08/Program.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Fragment.h" 3 | #include "Tree.h" 4 | #include 5 | #include 6 | 7 | namespace tiger { 8 | 9 | using CompileResult = boost::optional; 10 | 11 | CompileResult compileFile(const std::string &filename); 12 | 13 | CompileResult compile(const std::string &string); 14 | 15 | std::ostream & 16 | operator<<(std::ostream &ost, 17 | std::pair const &compileResult); 18 | 19 | } // namespace tiger 20 | -------------------------------------------------------------------------------- /Chapter08/Skipper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "warning_suppress.h" 3 | MSC_DIAG_OFF(4996 4459) 4 | #include 5 | MSC_DIAG_ON() 6 | 7 | namespace tiger { 8 | template 9 | class Skipper : public boost::spirit::qi::grammar { 10 | public: 11 | Skipper() : Skipper::base_type(skip) { 12 | namespace ascii = boost::spirit::ascii; 13 | 14 | using ascii::char_; 15 | ascii::space_type space; 16 | 17 | skip = space // tab/space/cr/lf 18 | | "/*" >> *(char_ - "*/") >> "*/" // comments 19 | ; 20 | } 21 | 22 | private: 23 | boost::spirit::qi::rule skip; 24 | }; 25 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter08/TempMap.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * temp.cpp - functions to create and manipulate Register variables which are 3 | * used in the IR tree representation before it has been determined 4 | * which variables are to go into registers. 5 | * 6 | */ 7 | 8 | #include "TempMap.h" 9 | #include 10 | #include 11 | 12 | namespace tiger { 13 | 14 | namespace temp { 15 | 16 | std::ostream &operator<<(std::ostream &ost, const Map &map) { 17 | for (const auto &it : map.m_map) { 18 | ost << "t" << it.first << " -> " << it.second << '\n'; 19 | } 20 | if (map.m_under) { 21 | ost << "---------\n"; 22 | ost << *map.m_under; 23 | } 24 | 25 | return ost; 26 | } 27 | 28 | void Map::layerOver(Map &&under) { 29 | if (m_under) { 30 | m_under->layerOver(std::move(under)); 31 | } else { 32 | m_under.reset(&under); 33 | } 34 | } 35 | 36 | Register Map::newTemp() { 37 | Register res{m_nextTemp}; 38 | m_map[res] = std::to_string(m_nextTemp++); 39 | return res; 40 | } 41 | 42 | boost::optional Map::lookup(const Register &t) { 43 | auto it = m_map.find(t); 44 | if (it != m_map.end()) 45 | return it->second; 46 | if (m_under) 47 | return m_under->lookup(t); 48 | return {}; 49 | } 50 | 51 | Label Map::newLabel() { 52 | static const auto MIN_LABEL = 0; 53 | static auto labels = MIN_LABEL; 54 | return Label{"L" + std::to_string(labels++)}; 55 | } 56 | 57 | Label Map::namedLabel(const std::string &name) { return Label{name}; } 58 | 59 | } // namespace temp 60 | } // namespace tiger 61 | -------------------------------------------------------------------------------- /Chapter08/TempMap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "strong_typedef.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace tiger { 10 | 11 | namespace temp { 12 | 13 | // a register is just a number 14 | using Register = helpers::strong_typedef; 15 | 16 | // a label is just a string 17 | using Label = helpers::strong_typedef; 18 | 19 | constexpr auto MIN_TEMP = 100; 20 | 21 | // A Map is just a table whose keys are Temp_temps and whose bindings 22 | // are strings. However, one mapping can be layered over another; if σ3 = 23 | // layer(σ1, σ2), this means that look(σ3, t) will first try look(σ1, t), and if 24 | // that fails it will try look(σ2, t).Also, enter(σ3, t, a) will have the effect 25 | // of entering t->a into σ2. 26 | class Map { 27 | public: 28 | void layerOver(Map &&under); 29 | Register newTemp(); 30 | boost::optional lookup(const Register &t); 31 | friend std::ostream &operator<<(std::ostream &ost, const Map &map); 32 | 33 | Label newLabel(); 34 | Label namedLabel(const std::string &name); 35 | 36 | private: 37 | std::unordered_map m_map; 38 | std::unique_ptr m_under; 39 | int m_nextTemp = MIN_TEMP; 40 | }; 41 | 42 | } // namespace temp 43 | } // namespace tiger 44 | -------------------------------------------------------------------------------- /Chapter08/Types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tiger { 9 | 10 | struct NilType {}; 11 | 12 | struct IntType {}; 13 | 14 | struct StringType {}; 15 | 16 | struct ArrayType; 17 | 18 | struct RecordType; 19 | 20 | struct NameType { 21 | std::string m_name; 22 | }; 23 | 24 | struct VoidType {}; 25 | 26 | using Type = 27 | boost::variant, 29 | boost::recursive_wrapper, NameType, VoidType>; 30 | 31 | struct NamedType { 32 | std::string m_name; 33 | Type m_type; 34 | }; 35 | 36 | struct ArrayType { 37 | NamedType m_elementType; 38 | }; 39 | 40 | struct RecordType { 41 | struct RecordField { 42 | std::string m_name; 43 | NamedType m_type; 44 | }; 45 | std::vector m_fields; 46 | }; 47 | 48 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter08/x64FastCall/CallingConvention.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../CallingConvention.h" 4 | 5 | namespace tiger { 6 | namespace frame { 7 | namespace x64FastCall { 8 | class CallingConvention final : public frame::CallingConvention { 9 | public: 10 | CallingConvention(temp::Map &tempMap); 11 | 12 | virtual std::unique_ptr 13 | createFrame(const temp::Label &name, const BoolList &formals) override; 14 | 15 | virtual int wordSize() const override; 16 | 17 | virtual temp::Register framePointer() const override; 18 | 19 | virtual temp::Register returnValue() const override; 20 | 21 | virtual ir::Expression 22 | accessFrame(const VariableAccess &access, 23 | const ir::Expression &framePtr) const override; 24 | 25 | virtual void allocateString(temp::Label label, 26 | const std::string &str) override; 27 | 28 | virtual ir::Expression 29 | externalCall(const std::string &name, 30 | const std::vector &args) override; 31 | }; 32 | } // namespace x64FastCall 33 | } // namespace frame 34 | } // namespace tiger 35 | -------------------------------------------------------------------------------- /Chapter08/x64FastCall/Frame.cpp: -------------------------------------------------------------------------------- 1 | #include "Frame.h" 2 | #include "variantMatch.h" 3 | #include 4 | #include 5 | 6 | namespace tiger { 7 | namespace frame { 8 | namespace x64FastCall { 9 | 10 | std::array Frame::m_regParams = { 11 | Registers::RCX, Registers::RDX, Registers::R8, Registers::R9}; 12 | 13 | Frame::Frame(temp::Map &tempMap, const temp::Label &name, 14 | const BoolList &formals) : 15 | frame::Frame(tempMap), 16 | m_name(name) { 17 | for (size_t i = 0; i < formals.size(); ++i) { 18 | auto varAccess = [this](bool escapes) -> VariableAccess { 19 | if (escapes || m_allocatedRegs == MAX_REGS) { 20 | auto res = InFrame{m_frameOffset}; 21 | m_frameOffset -= WORD_SIZE; 22 | return res; 23 | } 24 | 25 | assert(m_allocatedRegs < MAX_REGS); 26 | return InReg{reg(m_regParams[m_allocatedRegs++])}; 27 | }(formals[i]); 28 | m_formals.push_back(varAccess); 29 | } 30 | } 31 | 32 | temp::Label Frame::name() const { return m_name; } 33 | 34 | const AccessList &Frame::formals() const { return m_formals; } 35 | 36 | VariableAccess Frame::allocateLocal(bool escapes) { 37 | if (escapes) { 38 | auto res = InFrame{m_frameOffset}; 39 | m_frameOffset -= WORD_SIZE; 40 | return res; 41 | } 42 | 43 | return InReg{m_tempMap.newTemp()}; 44 | } 45 | 46 | ir::Statement Frame::procEntryExit1(const ir::Statement &body) { return body; } 47 | 48 | } // namespace x64FastCall 49 | } // namespace frame 50 | } // namespace tiger 51 | -------------------------------------------------------------------------------- /Chapter08/x64FastCall/Frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../Frame.h" 4 | #include "Registers.h" 5 | #include 6 | 7 | namespace tiger { 8 | namespace frame { 9 | namespace x64FastCall { 10 | // implements 11 | // https://docs.microsoft.com/en-gb/cpp/build/overview-of-x64-calling-conventions 12 | class Frame final : public frame::Frame { 13 | public: 14 | static const int WORD_SIZE = 8; 15 | 16 | Frame(temp::Map &tempMap, const temp::Label &name, const BoolList &formals); 17 | 18 | // Inherited via Frame 19 | virtual temp::Label name() const override; 20 | 21 | virtual const AccessList &formals() const override; 22 | 23 | virtual VariableAccess allocateLocal(bool escapes) override; 24 | 25 | virtual ir::Statement procEntryExit1(const ir::Statement &body) override; 26 | 27 | private: 28 | temp::Label m_name; 29 | AccessList m_formals; 30 | int m_frameOffset = -WORD_SIZE; 31 | // The __fastcall convention uses registers for the first four arguments 32 | static const size_t MAX_REGS = 4; 33 | static std::array m_regParams; 34 | size_t m_allocatedRegs = 0; 35 | }; 36 | } // namespace x64FastCall 37 | } // namespace frame 38 | } // namespace tiger 39 | -------------------------------------------------------------------------------- /Chapter08/x64FastCall/Registers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../TempMap.h" 3 | 4 | namespace tiger { 5 | namespace frame { 6 | namespace x64FastCall { 7 | 8 | enum class Registers { 9 | RAX, 10 | RDX, 11 | RCX, 12 | RBX, 13 | RSI, 14 | RDI, 15 | RBP, 16 | RSP, 17 | R8, 18 | R9, 19 | R10, 20 | R11, 21 | R12, 22 | R13, 23 | R14, 24 | R15, 25 | RIP, 26 | MM0, 27 | MM1, 28 | MM2, 29 | MM3, 30 | MM4, 31 | MM5, 32 | MM6, 33 | MM7, 34 | FP0, 35 | FP1, 36 | FP2, 37 | FP3, 38 | FP4, 39 | FP5, 40 | FP6, 41 | FP7, 42 | XMM8, 43 | XMM9, 44 | XMM10, 45 | XMM11, 46 | XMM12, 47 | XMM13, 48 | XMM14, 49 | XMM15, 50 | XMM16, 51 | XMM17, 52 | XMM18, 53 | XMM19, 54 | XMM20, 55 | XMM21, 56 | XMM22, 57 | XMM23, 58 | XMM24, 59 | XMM25, 60 | XMM26, 61 | XMM27, 62 | XMM28, 63 | XMM29, 64 | XMM30, 65 | XMM31, 66 | Size 67 | }; 68 | 69 | constexpr temp::Register reg(Registers reg) { 70 | return temp::Register{static_cast(reg)}; 71 | } 72 | 73 | static_assert(static_cast(Registers::Size) <= temp::MIN_TEMP, 74 | "MIN_TEMP should be greater than the greatest register"); 75 | } // namespace x64FastCall 76 | } // namespace frame 77 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter09/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CHAPTER Chapter09) 2 | 3 | macro(add_machine name) 4 | add_subdirectory(${name}) 5 | list(APPEND MACHINES ${name}) 6 | list(APPEND MACHINE_LIBRARIES ${CHAPTER}_${name}) 7 | endmacro() 8 | 9 | add_machine(m68k) 10 | add_machine(x64) 11 | 12 | foreach(machine ${MACHINES}) 13 | string(TOLOWER ${machine} machineLower) 14 | string(APPEND MACHINE_INCLUDES "#include \"${machine}/${machine}Machine.h\"\n") 15 | string(APPEND MACHINE_REGISTRATIONS "REGISTER_MACHINE(${machine}, tiger::${machineLower}::Machine)\n") 16 | endforeach() 17 | 18 | configure_file(MachineRegistration.h.in MachineRegistration.h) 19 | 20 | set(SOURCES Program.cpp SemanticAnalyzer.cpp TempMap.cpp EscapeAnalyser.cpp Translator.cpp Tree.cpp Canonicalizer.cpp Assembly.cpp 21 | CodeGenerator.cpp CallingConvention.cpp) 22 | set(HEADERS Program.h ErrorHandler.h ExpressionParser.h Skipper.h IdentifierParser.h DeclerationParser.h AbstractSyntaxTree.h 23 | Annotation.h StringParser.h SemanticAnalyzer.h Types.h TempMap.h Frame.h CallingConvention.h EscapeAnalyser.h Translator.h Tree.h 24 | Fragment.h Canonicalizer.h Assembly.h CodeGenerator.h MachineRegistrar.h) 25 | 26 | add_library(Chapter09 ${HEADERS} ${SOURCES}) 27 | 28 | target_include_directories(Chapter09 29 | PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} 30 | PRIVATE ${CMAKE_CURRENT_BINARY_DIR} 31 | ) 32 | 33 | if(MSVC) 34 | set_source_files_properties(Program.cpp PROPERTIES COMPILE_FLAGS /bigobj) 35 | endif() 36 | 37 | target_link_libraries(Chapter09 38 | PRIVATE 39 | ${MACHINE_LIBRARIES} 40 | PUBLIC 41 | Boost::boost 42 | includeHeaders 43 | range-v3::range-v3 44 | ) 45 | 46 | add_subdirectory(test) 47 | -------------------------------------------------------------------------------- /Chapter09/CallingConvention.cpp: -------------------------------------------------------------------------------- 1 | #include "CallingConvention.h" 2 | #include "Assembly.h" 3 | #include 4 | 5 | namespace tiger { 6 | namespace frame { 7 | 8 | ir::Expression 9 | CallingConvention::accessFrame(const VariableAccess &access, 10 | const ir::Expression &framePtr) const { 11 | using helpers::match; 12 | return match(access)( 13 | [](const frame::InReg &inReg) -> ir::Expression { return inReg.m_reg; }, 14 | [&framePtr](const frame::InFrame &inFrame) -> ir::Expression { 15 | return ir::MemoryAccess{ 16 | ir::BinaryOperation{ir::BinOp::PLUS, framePtr, inFrame.m_offset}}; 17 | }); 18 | } 19 | 20 | ir::Expression 21 | CallingConvention::externalCall(const temp::Label &name, 22 | const std::vector &args) { 23 | return ir::Call{name, args}; 24 | } 25 | 26 | assembly::Instructions 27 | CallingConvention::procEntryExit2(const assembly::Instructions &body) const { 28 | // appends a “sink” instruction to the function body to tell the 29 | // register allocator that certain registers are live at procedure exit 30 | namespace rv = ranges::view; 31 | auto calleeSaved = calleeSavedRegisters(); 32 | auto liveAtExit = rv::concat(calleeSaved, rv::single(returnValue()), 33 | rv::single(stackPointer())); 34 | #ifdef _MSC_VER 35 | auto res = body; 36 | res.push_back(assembly::Operation{{}, {}, liveAtExit}); 37 | return res; 38 | #else 39 | return ranges::view::concat( 40 | body, rv::single(assembly::Operation{{}, {}, liveAtExit})); 41 | #endif 42 | } 43 | 44 | } // namespace frame 45 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter09/CallingConvention.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Frame.h" 3 | 4 | namespace tiger { 5 | namespace assembly { 6 | struct Instruction; 7 | using Instructions = std::vector; 8 | using Registers = std::vector; 9 | } // namespace assembly 10 | 11 | namespace frame { 12 | class CallingConvention { 13 | public: 14 | virtual ~CallingConvention() = default; 15 | 16 | virtual std::unique_ptr createFrame(temp::Map &tempMap, 17 | const temp::Label &name, 18 | const BoolList &formals) = 0; 19 | virtual int wordSize() const = 0; 20 | 21 | virtual temp::Register framePointer() const = 0; 22 | 23 | virtual temp::Register returnValue() const = 0; 24 | 25 | virtual temp::Register stackPointer() const = 0; 26 | 27 | virtual assembly::Registers callDefinedRegisters() const = 0; 28 | 29 | virtual assembly::Registers calleeSavedRegisters() const = 0; 30 | 31 | virtual assembly::Registers argumentRegisters() const = 0; 32 | 33 | ir::Expression accessFrame(const VariableAccess &access, 34 | const ir::Expression &framePtr) const; 35 | 36 | ir::Expression externalCall(const temp::Label &name, 37 | const std::vector &args); 38 | 39 | // calculate register liveness 40 | assembly::Instructions 41 | procEntryExit2(const assembly::Instructions &body) const; 42 | }; 43 | } // namespace frame 44 | } // namespace tiger 45 | -------------------------------------------------------------------------------- /Chapter09/ErrorHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace tiger { 8 | template class ErrorHandler { 9 | public: 10 | template struct result { typedef void type; }; 11 | 12 | template 13 | void operator()(Message const &message, What const &what, 14 | Iterator err_pos) const { 15 | class TigerError : public std::logic_error { 16 | using std::logic_error::logic_error; 17 | }; 18 | const auto &pos = err_pos.get_position(); 19 | std::stringstream sst; 20 | sst << pos.file << "(" << pos.line << "," << pos.column << "): " << message 21 | << ": " << what << '\n'; 22 | sst << "'"; 23 | std::copy(err_pos.get_currentline_begin(), err_pos.get_currentline_end(), 24 | std::ostream_iterator(sst)); 25 | sst << "'\n"; 26 | sst << std::setw(pos.column) << " " 27 | << "^- here\n"; 28 | throw TigerError{sst.str()}; 29 | } 30 | }; 31 | } // namespace tiger 32 | -------------------------------------------------------------------------------- /Chapter09/Fragment.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Tree.h" 3 | #include 4 | 5 | namespace tiger { 6 | 7 | namespace frame { 8 | class Frame; 9 | } 10 | 11 | struct StringFragment { 12 | temp::Label m_label; 13 | std::string m_string; 14 | }; 15 | 16 | struct FunctionFragment { 17 | ir::Statement m_body; 18 | std::shared_ptr m_frame; 19 | }; 20 | 21 | using Fragment = boost::variant; 22 | using FragmentList = std::vector; 23 | } // namespace tiger 24 | -------------------------------------------------------------------------------- /Chapter09/Frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TempMap.h" 3 | #include "Tree.h" 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tiger { 9 | 10 | namespace assembly { 11 | struct Instruction; 12 | using Instructions = std::vector; 13 | } // namespace assembly 14 | 15 | namespace frame { 16 | 17 | using BoolList = boost::dynamic_bitset<>; 18 | 19 | struct InFrame { 20 | int m_offset; 21 | }; 22 | 23 | struct InReg { 24 | temp::Register m_reg; 25 | }; 26 | 27 | using VariableAccess = boost::variant; 28 | using AccessList = std::vector; 29 | 30 | class Frame { 31 | public: 32 | Frame(temp::Map &tempMap) : m_tempMap(tempMap) {} 33 | 34 | virtual ~Frame() noexcept = default; 35 | 36 | virtual temp::Label name() const = 0; 37 | virtual const AccessList &formals() const = 0; 38 | virtual VariableAccess allocateLocal(bool escapes) = 0; 39 | // move input parameters to the function frame 40 | // store and restore callee saved registers 41 | virtual ir::Statement procEntryExit1(const ir::Statement &body) const = 0; 42 | // add prolog and epilog 43 | virtual assembly::Instructions 44 | procEntryExit3(const assembly::Instructions &body) const = 0; 45 | 46 | protected: 47 | temp::Map &m_tempMap; 48 | }; 49 | 50 | } // namespace frame 51 | } // namespace tiger 52 | -------------------------------------------------------------------------------- /Chapter09/Machine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TempMap.h" 3 | #include 4 | 5 | namespace tiger { 6 | namespace frame { 7 | class CallingConvention; 8 | } 9 | 10 | namespace assembly { 11 | class CodeGenerator; 12 | } // namespace assembly 13 | 14 | class Machine { 15 | public: 16 | virtual ~Machine() = default; 17 | 18 | virtual temp::PredefinedRegisters predefinedRegisters() = 0; 19 | 20 | virtual frame::CallingConvention &callingConvention() = 0; 21 | virtual const frame::CallingConvention &callingConvention() const = 0; 22 | 23 | virtual assembly::CodeGenerator &codeGenerator() = 0; 24 | virtual const assembly::CodeGenerator &codeGenerator() const = 0; 25 | }; 26 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter09/MachineRegistrar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Machine.h" 3 | #include 4 | #include 5 | #include 6 | 7 | #define REGISTER_MACHINE(name, type) \ 8 | \ 9 | namespace { \ 10 | tiger::AutoRegistrar registered_##name(#name); \ 11 | } 12 | 13 | namespace tiger { 14 | 15 | using MachineRegistrar = 16 | std::map()>>; 17 | 18 | MachineRegistrar &machineRegistrar() { 19 | static MachineRegistrar registrar; 20 | return registrar; 21 | } 22 | 23 | struct NoSuchArchError : std::logic_error { 24 | using std::logic_error::logic_error; 25 | }; 26 | 27 | template 28 | inline void registerMachine(const std::string &arch, Creator &&creator) { 29 | machineRegistrar()[arch] = std::forward(creator); 30 | } 31 | 32 | template struct AutoRegistrar { 33 | AutoRegistrar(const std::string &arch) { 34 | registerMachine(arch, []() { return std::make_unique(); }); 35 | } 36 | }; 37 | 38 | inline std::unique_ptr createMachine(const std::string &arch) { 39 | auto it = machineRegistrar().find(arch); 40 | if (it == machineRegistrar().end()) { 41 | throw NoSuchArchError{"no machine named " + arch}; 42 | } 43 | return it->second(); 44 | } 45 | 46 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter09/MachineRegistration.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "MachineRegistrar.h" 3 | @MACHINE_INCLUDES@ 4 | 5 | @MACHINE_REGISTRATIONS@ -------------------------------------------------------------------------------- /Chapter09/Program.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Assembly.h" 3 | #include "Fragment.h" 4 | #include 5 | #include 6 | 7 | namespace tiger { 8 | 9 | using CompileResult = boost::optional; 10 | 11 | CompileResult compileFile(const std::string &arch, const std::string &filename); 12 | 13 | CompileResult compile(const std::string &arch, const std::string &string); 14 | 15 | } // namespace tiger 16 | -------------------------------------------------------------------------------- /Chapter09/Skipper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "warning_suppress.h" 3 | MSC_DIAG_OFF(4996 4459) 4 | #include 5 | MSC_DIAG_ON() 6 | 7 | namespace tiger { 8 | template 9 | class Skipper : public boost::spirit::qi::grammar { 10 | public: 11 | Skipper() : Skipper::base_type(skip) { 12 | namespace ascii = boost::spirit::ascii; 13 | 14 | using ascii::char_; 15 | ascii::space_type space; 16 | 17 | skip = space // tab/space/cr/lf 18 | | "/*" >> *(char_ - "*/") >> "*/" // comments 19 | ; 20 | } 21 | 22 | private: 23 | boost::spirit::qi::rule skip; 24 | }; 25 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter09/TempMap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "strong_typedef.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace tiger { 11 | 12 | namespace temp { 13 | 14 | // a register is just a number 15 | using Register = helpers::strong_typedef; 16 | 17 | // a label is just a string 18 | using Label = helpers::strong_typedef; 19 | 20 | using PredefinedRegisters = std::vector>; 21 | 22 | constexpr auto MIN_TEMP = 100; 23 | 24 | // A Map is just a table whose keys are Temp_temps and whose bindings 25 | // are strings. However, one mapping can be layered over another; if σ3 = 26 | // layer(σ1, σ2), this means that look(σ3, t) will first try look(σ1, t), and if 27 | // that fails it will try look(σ2, t).Also, enter(σ3, t, a) will have the effect 28 | // of entering t->a into σ2. 29 | class Map { 30 | public: 31 | Map(const PredefinedRegisters &predefinedRegisters = {}); 32 | void layerOver(Map &&under); 33 | Register newTemp(); 34 | boost::optional lookup(const Register &t) const; 35 | friend std::ostream &operator<<(std::ostream &ost, const Map &map); 36 | 37 | Label newLabel(); 38 | Label namedLabel(const std::string &name); 39 | 40 | private: 41 | std::unordered_map m_map; 42 | std::unique_ptr m_under; 43 | int m_nextTemp = MIN_TEMP; 44 | }; 45 | 46 | } // namespace temp 47 | } // namespace tiger 48 | -------------------------------------------------------------------------------- /Chapter09/Types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tiger { 9 | 10 | struct NilType {}; 11 | 12 | struct IntType {}; 13 | 14 | struct StringType {}; 15 | 16 | struct ArrayType; 17 | 18 | struct RecordType; 19 | 20 | struct NameType { 21 | std::string m_name; 22 | }; 23 | 24 | struct VoidType {}; 25 | 26 | using Type = 27 | boost::variant, 29 | boost::recursive_wrapper, NameType, VoidType>; 30 | 31 | struct NamedType { 32 | std::string m_name; 33 | Type m_type; 34 | }; 35 | 36 | struct ArrayType { 37 | NamedType m_elementType; 38 | }; 39 | 40 | struct RecordType { 41 | struct RecordField { 42 | std::string m_name; 43 | NamedType m_type; 44 | }; 45 | std::vector m_fields; 46 | }; 47 | 48 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter09/m68k/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES m68kFrame.cpp m68kCallingConvention.cpp m68kCodeGenerator.cpp m68kMachine.cpp) 2 | set(HEADERS m68kFrame.h m68kCallingConvention.h m68kCodeGenerator.h m68kRegisters.h m68kMachine.h) 3 | 4 | add_library(${CHAPTER}_m68k ${HEADERS} ${SOURCES}) 5 | 6 | set_target_properties(${CHAPTER}_m68k PROPERTIES OUTPUT_NAME m68k) 7 | 8 | target_include_directories(${CHAPTER}_m68k PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..) 9 | 10 | target_link_libraries(${CHAPTER}_m68k 11 | PRIVATE 12 | includeHeaders 13 | Boost::boost 14 | ) -------------------------------------------------------------------------------- /Chapter09/m68k/m68kCallingConvention.cpp: -------------------------------------------------------------------------------- 1 | #include "m68kCallingConvention.h" 2 | #include "Assembly.h" 3 | #include "m68kFrame.h" 4 | #include "m68kRegisters.h" 5 | #include "variantMatch.h" 6 | 7 | namespace tiger { 8 | namespace frame { 9 | namespace m68k { 10 | 11 | std::unique_ptr 12 | CallingConvention::createFrame(temp::Map &tempMap, const temp::Label &name, 13 | const BoolList &formals) { 14 | return std::make_unique(tempMap, name, formals); 15 | } 16 | 17 | int CallingConvention::wordSize() const { return Frame::WORD_SIZE; } 18 | 19 | temp::Register CallingConvention::framePointer() const { 20 | return reg(Registers::A6); 21 | } 22 | 23 | temp::Register CallingConvention::returnValue() const { 24 | return reg(Registers::D0); 25 | } 26 | 27 | temp::Register CallingConvention::stackPointer() const { 28 | return reg(Registers::A7); 29 | } 30 | 31 | assembly::Registers CallingConvention::callDefinedRegisters() const { 32 | return {reg(Registers::A0), reg(Registers::A1), reg(Registers::D0), 33 | reg(Registers::D1)}; 34 | } 35 | 36 | assembly::Registers CallingConvention::calleeSavedRegisters() const { 37 | return {reg(Registers::A2), reg(Registers::A3), reg(Registers::A4), 38 | reg(Registers::A5), reg(Registers::A6), reg(Registers::A7), 39 | reg(Registers::D2), reg(Registers::D3), reg(Registers::D4), 40 | reg(Registers::D5), reg(Registers::D6), reg(Registers::D7)}; 41 | } 42 | 43 | assembly::Registers CallingConvention::argumentRegisters() const { return {}; } 44 | 45 | } // namespace m68k 46 | } // namespace frame 47 | } // namespace tiger 48 | -------------------------------------------------------------------------------- /Chapter09/m68k/m68kCallingConvention.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CallingConvention.h" 4 | 5 | namespace tiger { 6 | namespace frame { 7 | namespace m68k { 8 | class CallingConvention final : public frame::CallingConvention { 9 | public: 10 | std::unique_ptr createFrame(temp::Map &tempMap, 11 | const temp::Label &name, 12 | const BoolList &formals) override; 13 | 14 | int wordSize() const override; 15 | 16 | temp::Register framePointer() const override; 17 | 18 | temp::Register returnValue() const override; 19 | 20 | temp::Register stackPointer() const override; 21 | 22 | assembly::Registers callDefinedRegisters() const override; 23 | 24 | assembly::Registers calleeSavedRegisters() const override; 25 | 26 | assembly::Registers argumentRegisters() const override; 27 | }; 28 | } // namespace m68k 29 | } // namespace frame 30 | } // namespace tiger 31 | -------------------------------------------------------------------------------- /Chapter09/m68k/m68kCodeGenerator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "CodeGenerator.h" 3 | 4 | namespace tiger { 5 | namespace assembly { 6 | namespace m68k { 7 | class CodeGenerator : public assembly::CodeGenerator { 8 | public: 9 | CodeGenerator(frame::CallingConvention &callingConvention); 10 | 11 | Instructions translateString(const temp::Label &label, 12 | const std::string &string, 13 | temp::Map &tempMap) override; 14 | 15 | private: 16 | Instructions translateArgs(const std::vector &args, 17 | const temp::Map &tempMap) const override; 18 | }; 19 | } // namespace m68k 20 | } // namespace assembly 21 | } // namespace tiger 22 | -------------------------------------------------------------------------------- /Chapter09/m68k/m68kFrame.cpp: -------------------------------------------------------------------------------- 1 | #include "m68kFrame.h" 2 | #include "Assembly.h" 3 | #include "variantMatch.h" 4 | #include 5 | #include 6 | 7 | namespace tiger { 8 | namespace frame { 9 | namespace m68k { 10 | 11 | Frame::Frame(temp::Map &tempMap, const temp::Label &name, 12 | const BoolList &formals) : 13 | frame::Frame(tempMap), 14 | m_name(name) { 15 | for (size_t i = 0; i < formals.size(); ++i) { 16 | m_formals.push_back(InFrame{m_frameOffset}); 17 | m_frameOffset -= WORD_SIZE; 18 | } 19 | } 20 | 21 | temp::Label Frame::name() const { return m_name; } 22 | 23 | const AccessList &Frame::formals() const { return m_formals; } 24 | 25 | VariableAccess Frame::allocateLocal(bool escapes) { 26 | if (escapes) { 27 | auto res = InFrame{m_frameOffset}; 28 | m_frameOffset -= WORD_SIZE; 29 | return res; 30 | } 31 | 32 | return InReg{m_tempMap.newTemp()}; 33 | } 34 | 35 | ir::Statement Frame::procEntryExit1(const ir::Statement &body) const { 36 | return body; 37 | } 38 | 39 | assembly::Instructions 40 | Frame::procEntryExit3(const assembly::Instructions &body) const { 41 | return body; 42 | } 43 | 44 | } // namespace m68k 45 | } // namespace frame 46 | } // namespace tiger 47 | -------------------------------------------------------------------------------- /Chapter09/m68k/m68kFrame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Frame.h" 4 | #include "m68kRegisters.h" 5 | #include 6 | 7 | namespace tiger { 8 | namespace frame { 9 | namespace m68k { 10 | // implements 11 | // https://docs.microsoft.com/en-gb/cpp/build/overview-of-x64-calling-conventions 12 | class Frame final : public frame::Frame { 13 | public: 14 | static const int WORD_SIZE = 4; 15 | 16 | Frame(temp::Map &tempMap, const temp::Label &name, const BoolList &formals); 17 | 18 | // Inherited via Frame 19 | temp::Label name() const override; 20 | 21 | const AccessList &formals() const override; 22 | 23 | VariableAccess allocateLocal(bool escapes) override; 24 | 25 | ir::Statement procEntryExit1(const ir::Statement &body) const override; 26 | 27 | assembly::Instructions 28 | procEntryExit3(const assembly::Instructions &body) const override; 29 | 30 | private: 31 | temp::Label m_name; 32 | AccessList m_formals; 33 | int m_frameOffset = -WORD_SIZE; 34 | }; 35 | } // namespace m68k 36 | } // namespace frame 37 | } // namespace tiger 38 | -------------------------------------------------------------------------------- /Chapter09/m68k/m68kMachine.cpp: -------------------------------------------------------------------------------- 1 | #include "m68kMachine.h" 2 | #include "Tree.h" 3 | #include "m68kRegisters.h" 4 | 5 | namespace tiger { 6 | namespace m68k { 7 | 8 | frame::CallingConvention &Machine::callingConvention() { 9 | return m_callingConvention; 10 | } 11 | const frame::CallingConvention &Machine::callingConvention() const { 12 | return m_callingConvention; 13 | } 14 | assembly::CodeGenerator &Machine::codeGenerator() { return m_codeGenerator; } 15 | const assembly::CodeGenerator &Machine::codeGenerator() const { 16 | return m_codeGenerator; 17 | } 18 | temp::PredefinedRegisters Machine::predefinedRegisters() { 19 | using namespace tiger::frame::m68k; 20 | return {{reg(Registers::D0), "D0"}, 21 | {reg(Registers::D1), "D1"}, 22 | {reg(Registers::D2), "D2"}, 23 | {reg(Registers::D3), "D3"}, 24 | {reg(Registers::D4), "D4"}, 25 | {reg(Registers::D5), "D5"}, 26 | {reg(Registers::D6), "D6"}, 27 | {reg(Registers::D7), "D7"}, 28 | {reg(Registers::A0), "A0"}, 29 | {reg(Registers::A1), "A1"}, 30 | {reg(Registers::A2), "A2"}, 31 | {reg(Registers::A3), "A3"}, 32 | {reg(Registers::A4), "A4"}, 33 | {reg(Registers::A5), "A5"}, 34 | {reg(Registers::A6), "A6"}, 35 | {reg(Registers::A7), "A7"} 36 | 37 | }; 38 | } 39 | } // namespace m68k 40 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter09/m68k/m68kMachine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Machine.h" 4 | #include "m68kCallingConvention.h" 5 | #include "m68kCodeGenerator.h" 6 | 7 | namespace tiger { 8 | namespace m68k { 9 | 10 | class Machine : public tiger::Machine { 11 | public: 12 | // Inherited via Machine 13 | virtual frame::CallingConvention &callingConvention() override; 14 | virtual const frame::CallingConvention &callingConvention() const override; 15 | 16 | virtual assembly::CodeGenerator &codeGenerator() override; 17 | virtual const assembly::CodeGenerator &codeGenerator() const override; 18 | 19 | private: 20 | frame::m68k::CallingConvention m_callingConvention; 21 | assembly::m68k::CodeGenerator m_codeGenerator{m_callingConvention}; 22 | 23 | // Inherited via Machine 24 | virtual temp::PredefinedRegisters predefinedRegisters() override; 25 | }; 26 | } // namespace m68k 27 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter09/m68k/m68kRegisters.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TempMap.h" 3 | 4 | namespace tiger { 5 | namespace frame { 6 | namespace m68k { 7 | 8 | enum class Registers { 9 | D0, 10 | D1, 11 | D2, 12 | D3, 13 | D4, 14 | D5, 15 | D6, 16 | D7, 17 | A0, 18 | A1, 19 | A2, 20 | A3, 21 | A4, 22 | A5, 23 | A6, 24 | A7, 25 | Size 26 | }; 27 | 28 | constexpr temp::Register reg(Registers reg) { 29 | return temp::Register{static_cast(reg)}; 30 | } 31 | 32 | static_assert(static_cast(Registers::Size) <= temp::MIN_TEMP, 33 | "MIN_TEMP should be greater than the greatest register"); 34 | } // namespace m68k 35 | } // namespace frame 36 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter09/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(${CHAPTER}_testMain STATIC Test.h TestMain.cpp checkedCompile.cpp) 2 | 3 | set_target_properties(${CHAPTER}_testMain PROPERTIES OUTPUT_NAME testMain) 4 | 5 | target_link_libraries(${CHAPTER}_testMain PUBLIC 6 | Catch2::Catch 7 | Microsoft.GSL::GSL 8 | Boost::filesystem 9 | Boost::system 10 | includeHeaders 11 | ${CHAPTER} 12 | ) 13 | 14 | function(add_chapter_test name) 15 | add_executable(${CHAPTER}_${name} ${name}.cpp) 16 | 17 | set_target_properties(${CHAPTER}_${name} PROPERTIES OUTPUT_NAME ${name}) 18 | 19 | target_link_libraries(${CHAPTER}_${name} 20 | PRIVATE 21 | ${CHAPTER}_testMain 22 | ) 23 | 24 | parse_unit_tests(${CHAPTER}_${name} --timeout 20 ARCHS m68k x64) 25 | endfunction() 26 | 27 | add_chapter_test(compileFiles) 28 | add_chapter_test(lvalue) 29 | add_chapter_test(for) 30 | add_chapter_test(integer) 31 | add_chapter_test(comparison) 32 | add_chapter_test(typeDeclarations) 33 | add_chapter_test(boolean) 34 | add_chapter_test(array) 35 | add_chapter_test(nil) 36 | add_chapter_test(functionCall) 37 | add_chapter_test(string) 38 | add_chapter_test(assignment) 39 | add_chapter_test(arithmetic) 40 | add_chapter_test(ifThen) 41 | add_chapter_test(while) 42 | add_chapter_test(library) 43 | add_chapter_test(record) 44 | add_chapter_test(sequence) 45 | add_chapter_test(functionDeclarations) 46 | add_chapter_test(break) -------------------------------------------------------------------------------- /Chapter09/test/TestMain.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_RUNNER 2 | #include 3 | 4 | std::string arch; 5 | 6 | int main(int argc, char *argv[]) { 7 | Catch::Session session; // There must be exactly one instance 8 | 9 | // Build a new parser on top of Catch's 10 | using namespace Catch::clara; 11 | auto cli = 12 | session.cli() // Get Catch's composite command line parser 13 | | Opt( 14 | [&](const std::string &val) { 15 | if (val == "m68k" || val == "x64") { 16 | arch = val; 17 | return detail::ParserResult::ok(detail::ParseResultType::Matched); 18 | } 19 | 20 | return detail::ParserResult::runtimeError("unknown architecture " 21 | + val); 22 | }, 23 | "architecture") // bind variable to a new option, with a hint string 24 | ["--arch"] // the option names it will respond to 25 | ("target architecture: x64/m68k") 26 | .required(); // description string for the help 27 | // output 28 | 29 | // Now pass the new composite back to Catch so it uses that 30 | session.cli(cli); 31 | 32 | return session.run(argc, argv); 33 | } -------------------------------------------------------------------------------- /Chapter09/test/arithmetic.cpp: -------------------------------------------------------------------------------- 1 | #include "Test.h" 2 | #include 3 | 4 | TEST_CASE("arithmetic") { 5 | static const std::pair operations[] = { 6 | {"+", ir::BinOp::PLUS}, 7 | {"-", ir::BinOp::MINUS}, 8 | {"*", ir::BinOp::MUL}, 9 | {"/", ir::BinOp::DIV}}; 10 | 11 | for (const auto &op : operations) { 12 | SECTION(Catch::StringMaker::convert(op.second)) { 13 | auto program = checkedCompile(boost::str(boost::format(R"( 14 | let 15 | var i : int := 2 %1% 3 16 | in 17 | i 18 | end 19 | )") % op.first)); 20 | OptReg regs[4]; 21 | OptLabel end; 22 | checkProgram(program, checkMain(), 23 | checkMove(checkReg(regs[0]), checkImm(2)), 24 | checkMove(checkReg(regs[1]), checkImm(3)), 25 | checkBinaryOperation(op.second, checkReg(regs[0]), 26 | checkReg(regs[1]), regs[2]), 27 | checkMove(checkReg(regs[3]), // move result of 2 op 3 to r 28 | checkReg(regs[2])), 29 | checkMove( // return i 30 | returnReg(), checkReg(regs[3])), 31 | branchToEnd(end)); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /Chapter09/test/array.cpp: -------------------------------------------------------------------------------- 1 | #include "Test.h" 2 | 3 | TEST_CASE("array") { 4 | auto program = checkedCompile(R"( 5 | let 6 | type t = array of int 7 | in 8 | t[2] of 3 9 | end 10 | )"); 11 | OptReg regs[4]; 12 | OptLabel end; 13 | checkProgram( 14 | program, checkMain(), checkMove(checkReg(regs[0]), checkImm(wordSize())), 15 | checkMove(checkReg(regs[1]), checkImm(2)), 16 | checkBinaryOperation(ir::BinOp::MUL, checkReg(regs[0]), checkReg(regs[1]), 17 | regs[2]), 18 | checkCall("malloc", checkArg(0, checkReg(regs[2]))), 19 | checkMove( // move result of malloc(array_size) to r 20 | checkReg(regs[3]), returnReg()), 21 | checkCall( // call initArray(array_size, init_val) 22 | "initArray", checkArg(0, checkImm(2)) > checkArg(1, checkImm(3))), 23 | checkMove(returnReg(), checkReg(regs[3])), branchToEnd(end)); 24 | } -------------------------------------------------------------------------------- /Chapter09/test/assignment.cpp: -------------------------------------------------------------------------------- 1 | #include "Test.h" 2 | 3 | TEST_CASE("assignment") { 4 | auto program = checkedCompile(R"( 5 | let 6 | var a := 3 7 | var b := 0 8 | in 9 | b := a 10 | end 11 | )"); 12 | OptReg a, b; 13 | OptLabel end; 14 | checkProgram(program, checkMain(), 15 | checkMove( // move 3 to a 16 | checkReg(a), checkImm(3)), 17 | checkMove( // move 0 to b 18 | checkReg(b), checkImm(0)), 19 | checkMove( // move a to b 20 | checkReg(b), checkReg(a)), 21 | checkMove( // return 0 22 | returnReg(), checkImm(0)), 23 | branchToEnd(end)); 24 | } -------------------------------------------------------------------------------- /Chapter09/test/checkedCompile.cpp: -------------------------------------------------------------------------------- 1 | #include "Test.h" 2 | #include "Program.h" 3 | #include 4 | 5 | std::string checkedCompile(const std::string &string) { 6 | auto res = tiger::compile(arch, string); 7 | REQUIRE(res); 8 | return *res; 9 | } -------------------------------------------------------------------------------- /Chapter09/test/compileFiles.cpp: -------------------------------------------------------------------------------- 1 | #include "Program.h" 2 | #include "testsHelper.h" 3 | #include 4 | #include 5 | 6 | extern std::string arch; 7 | 8 | TEST_CASE("compile test files") { 9 | namespace fs = boost::filesystem; 10 | tiger::forEachTigerTest( 11 | [](const fs::path &filepath, bool parseError, bool compilationError) { 12 | SECTION(filepath.filename().string()) { 13 | if (parseError || compilationError) { 14 | REQUIRE_FALSE(tiger::compileFile(arch, filepath.string())); 15 | } else { 16 | REQUIRE(tiger::compileFile(arch, filepath.string())); 17 | } 18 | } 19 | }); 20 | } -------------------------------------------------------------------------------- /Chapter09/test/for.cpp: -------------------------------------------------------------------------------- 1 | #include "Test.h" 2 | 3 | TEST_CASE("for") { 4 | auto program = checkedCompile(R"( 5 | let 6 | function f(a: int) = () 7 | in 8 | for a := 0 to 4 do f(a) 9 | end 10 | )"); 11 | OptReg regs[4], staticLink, temps[2]; 12 | OptLabel functionLabel, loopStart, loopDone, functionEnd, end; 13 | checkProgram( 14 | program, checkLabel(functionLabel), ':', // function label 15 | checkMove( // body 16 | returnReg(), checkImm(0)), 17 | branchToEnd(functionEnd), checkMain(), 18 | checkMove( // move 0 to a 19 | checkReg(regs[0]), checkImm(0)), 20 | checkMove( // move 4 to limit 21 | checkReg(regs[1]), checkImm(4)), 22 | checkConditionalJump( // skip the loop if a , limit 23 | ir::RelOp::GT, regs[0], regs[1], loopDone), 24 | checkLabel(loopStart), ':', 25 | checkStaticLink(staticLink, temps), 26 | checkCall(checkLabel(functionLabel), checkArg(0, checkReg(staticLink)) 27 | > checkArg(1, checkReg(regs[0]))), 28 | checkMove(checkReg(regs[2]), checkImm(1)), 29 | checkBinaryOperation(ir::BinOp::PLUS, checkReg(regs[0]), checkReg(regs[2]), 30 | regs[3]), 31 | checkMove( // a := a + 1 32 | checkReg(regs[0]), checkReg(regs[3])), 33 | checkConditionalJump(ir::RelOp::LT, regs[0], regs[1], loopStart), 34 | checkLabel(loopDone), ':', checkMove(returnReg(), checkImm(0)), 35 | branchToEnd(end)); 36 | } -------------------------------------------------------------------------------- /Chapter09/test/functionCall.cpp: -------------------------------------------------------------------------------- 1 | #include "Test.h" 2 | 3 | TEST_CASE("function call") { 4 | OptLabel functionLabel, functionEnd, end; 5 | OptReg temps[2]; 6 | OptReg staticLink; 7 | 8 | SECTION("with no arguments") { 9 | auto program = checkedCompile(R"( 10 | let 11 | function f() = () 12 | in 13 | f() 14 | end 15 | )"); 16 | 17 | checkProgram(program, checkLabel(functionLabel), ':', // function label 18 | checkMove( // body 19 | returnReg(), checkImm(0)), 20 | branchToEnd(functionEnd), checkMain(), 21 | checkStaticLink(staticLink, temps), 22 | checkCall(//call 23 | checkLabel(functionLabel), checkArg(0, checkReg(staticLink))), 24 | branchToEnd(end)); 25 | } 26 | 27 | SECTION("with arguments") { 28 | auto program = checkedCompile(R"( 29 | let 30 | function f(a: int) = () 31 | in 32 | f(2) 33 | end 34 | )"); 35 | checkProgram( 36 | program, checkLabel(functionLabel), ':', // function label, 37 | checkMove( // body 38 | returnReg(), checkImm(0)), 39 | branchToEnd(functionEnd), checkMain(), 40 | checkStaticLink(staticLink, temps), 41 | checkCall( // call 42 | checkLabel(functionLabel), checkArg(0, checkReg(staticLink)) > checkArg(1, checkImm(2))), 43 | branchToEnd(end)); 44 | } 45 | } -------------------------------------------------------------------------------- /Chapter09/test/integer.cpp: -------------------------------------------------------------------------------- 1 | #include "Test.h" 2 | 3 | TEST_CASE("integer") { 4 | OptLabel end; 5 | auto program = checkedCompile("42"); 6 | checkProgram(program, checkMain(), checkMove(returnReg(), checkImm(42)), 7 | branchToEnd(end)); 8 | } -------------------------------------------------------------------------------- /Chapter09/test/nil.cpp: -------------------------------------------------------------------------------- 1 | #include "Test.h" 2 | 3 | TEST_CASE("nil") { 4 | OptLabel end; 5 | auto program = checkedCompile("nil"); 6 | checkProgram(program, checkMain(), checkMove(returnReg(), checkImm(0)), 7 | branchToEnd(end)); 8 | } -------------------------------------------------------------------------------- /Chapter09/test/record.cpp: -------------------------------------------------------------------------------- 1 | #include "Test.h" 2 | 3 | TEST_CASE("record") { 4 | OptLabel end; 5 | SECTION("empty") { 6 | auto program = checkedCompile(R"( 7 | let 8 | type t = {} 9 | in 10 | t{} 11 | end 12 | )"); 13 | OptReg regs[2]; 14 | checkProgram(program, checkMain(), 15 | checkCall("malloc", checkArg(0, checkImm(0))), 16 | checkMove( // move result of malloc(record_size) to 17 | checkReg(regs[1]), returnReg()), 18 | checkMove(returnReg(), checkReg(regs[1])), branchToEnd(end)); 19 | } 20 | 21 | SECTION("not empty") { 22 | auto program = checkedCompile(R"( 23 | let 24 | type t = {a: int, b: string} 25 | in 26 | t{a = 2, b = "hello"} 27 | end 28 | )"); 29 | 30 | OptReg regs[4], temps[2][4]; 31 | OptLabel stringLabel; 32 | checkProgram( 33 | program, checkStringInit(stringLabel, R"("hello")"), checkMain(), 34 | checkCall("malloc", checkArg(0, checkImm(2 * wordSize()))), 35 | checkMove(checkReg(regs[1]), // move result of malloc(record_size) to r 36 | returnReg()), 37 | checkMemberAccess(regs[1], 0, regs[2], temps[0]), 38 | checkMove(checkReg(regs[2]), // init first member with 2 39 | checkImm(2)), 40 | checkMemberAccess(regs[1], 1, regs[3], temps[1]), 41 | checkMove(checkReg(regs[3]), // init second member with "hello" 42 | checkString(stringLabel)), 43 | checkMove(returnReg(), checkReg(regs[1])), 44 | branchToEnd(end)); 45 | } 46 | } -------------------------------------------------------------------------------- /Chapter09/test/sequence.cpp: -------------------------------------------------------------------------------- 1 | #include "Test.h" 2 | 3 | TEST_CASE("sequence") { 4 | OptLabel end; 5 | SECTION("empty") { 6 | auto program = checkedCompile("()"); 7 | checkProgram(program, checkMain(), checkMove(returnReg(), checkImm(0)), 8 | branchToEnd(end)); 9 | } 10 | 11 | SECTION("single") { 12 | auto program = checkedCompile("(42)"); 13 | checkProgram(program, checkMain(), checkMove(returnReg(), checkImm(42)), 14 | branchToEnd(end)); 15 | } 16 | 17 | SECTION("multiple") { 18 | // no-op expressions are removed 19 | auto program = checkedCompile(R"((1;"two";flush()))"); 20 | OptReg temps[2]; 21 | OptLabel strLabel; 22 | OptReg staticLink; 23 | checkProgram(program, checkStringInit(strLabel, R"("two")"), checkMain(), 24 | checkStaticLink(staticLink, temps), 25 | checkCall("flush", checkArg(0, checkReg(staticLink))), 26 | branchToEnd(end)); 27 | } 28 | } -------------------------------------------------------------------------------- /Chapter09/test/string.cpp: -------------------------------------------------------------------------------- 1 | #include "Test.h" 2 | 3 | TEST_CASE("string") { 4 | auto str = R"("\tHello \"World\"!\n")"; 5 | auto program = checkedCompile(str); 6 | OptLabel stringLabel, end; 7 | checkProgram(program, checkStringInit(stringLabel, str), checkMain(), 8 | checkMove(returnReg(), checkString(stringLabel)), 9 | branchToEnd(end)); 10 | } -------------------------------------------------------------------------------- /Chapter09/x64/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCES x64Frame.cpp x64CallingConvention.cpp x64CodeGenerator.cpp x64Machine.cpp) 2 | set(HEADERS x64Frame.h x64CallingConvention.h x64CodeGenerator.h x64Registers.h x64Machine.h) 3 | 4 | add_library(${CHAPTER}_x64 ${HEADERS} ${SOURCES}) 5 | 6 | set_target_properties(${CHAPTER}_x64 PROPERTIES OUTPUT_NAME x64) 7 | 8 | target_include_directories(${CHAPTER}_x64 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..) 9 | 10 | target_link_libraries(${CHAPTER}_x64 11 | PRIVATE 12 | includeHeaders 13 | Boost::boost 14 | ) -------------------------------------------------------------------------------- /Chapter09/x64/x64CallingConvention.cpp: -------------------------------------------------------------------------------- 1 | #include "x64CallingConvention.h" 2 | #include "x64Frame.h" 3 | #include "x64Registers.h" 4 | 5 | namespace tiger { 6 | namespace frame { 7 | namespace x64 { 8 | 9 | std::unique_ptr 10 | CallingConvention::createFrame(temp::Map &tempMap, const temp::Label &name, 11 | const BoolList &formals) { 12 | return std::make_unique(tempMap, name, formals); 13 | } 14 | 15 | int CallingConvention::wordSize() const { return Frame::WORD_SIZE; } 16 | 17 | temp::Register CallingConvention::framePointer() const { 18 | return reg(Registers::RBP); 19 | } 20 | 21 | temp::Register CallingConvention::returnValue() const { 22 | return reg(Registers::RAX); 23 | } 24 | 25 | temp::Register CallingConvention::stackPointer() const { 26 | return reg(Registers::RSP); 27 | } 28 | 29 | std::vector CallingConvention::callDefinedRegisters() const { 30 | return {reg(Registers::RAX), reg(Registers::RCX), reg(Registers::RDX), 31 | reg(Registers::R8), reg(Registers::R9), reg(Registers::R10), 32 | reg(Registers::R11)}; 33 | } 34 | 35 | assembly::Registers CallingConvention::calleeSavedRegisters() const { 36 | return {reg(Registers::RBX), reg(Registers::RBP), reg(Registers::RDI), 37 | reg(Registers::RSI), reg(Registers::R12), reg(Registers::R13), 38 | reg(Registers::R14), reg(Registers::R15)}; 39 | } 40 | 41 | assembly::Registers CallingConvention::argumentRegisters() const { 42 | return {reg(Registers::RCX), reg(Registers::RDX), reg(Registers::R8), 43 | reg(Registers::R9)}; 44 | } 45 | 46 | } // namespace x64 47 | } // namespace frame 48 | } // namespace tiger 49 | -------------------------------------------------------------------------------- /Chapter09/x64/x64CallingConvention.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "CallingConvention.h" 4 | 5 | namespace tiger { 6 | namespace frame { 7 | namespace x64 { 8 | class CallingConvention final : public frame::CallingConvention { 9 | public: 10 | std::unique_ptr createFrame(temp::Map &tempMap, 11 | const temp::Label &name, 12 | const BoolList &formals) override; 13 | 14 | int wordSize() const override; 15 | 16 | temp::Register framePointer() const override; 17 | 18 | temp::Register returnValue() const override; 19 | 20 | temp::Register stackPointer() const override; 21 | 22 | std::vector callDefinedRegisters() const override; 23 | 24 | assembly::Registers calleeSavedRegisters() const override; 25 | 26 | assembly::Registers argumentRegisters() const override; 27 | }; 28 | } // namespace x64 29 | } // namespace frame 30 | } // namespace tiger 31 | -------------------------------------------------------------------------------- /Chapter09/x64/x64CodeGenerator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "CodeGenerator.h" 3 | 4 | namespace tiger { 5 | namespace assembly { 6 | namespace x64 { 7 | class CodeGenerator : public assembly::CodeGenerator { 8 | public: 9 | CodeGenerator(frame::CallingConvention &callingConvention); 10 | 11 | Instructions translateString(const temp::Label &label, 12 | const std::string &string, 13 | temp::Map &tempMap) override; 14 | 15 | Instructions translateArgs(const std::vector &args, 16 | const temp::Map &tempMap) const override; 17 | }; 18 | } // namespace x64 19 | } // namespace assembly 20 | } // namespace tiger 21 | -------------------------------------------------------------------------------- /Chapter09/x64/x64Frame.cpp: -------------------------------------------------------------------------------- 1 | #include "x64Frame.h" 2 | #include "Assembly.h" 3 | #include "variantMatch.h" 4 | #include 5 | #include 6 | 7 | namespace tiger { 8 | namespace frame { 9 | namespace x64 { 10 | 11 | Frame::Frame(temp::Map &tempMap, const temp::Label &name, 12 | const BoolList &formals) : 13 | frame::Frame(tempMap), 14 | m_name(name) { 15 | for (size_t i = 0; i < formals.size(); ++i) { 16 | auto varAccess = [this](bool escapes) -> VariableAccess { 17 | if (escapes || m_allocatedRegs == MAX_REGS) { 18 | auto res = InFrame{m_frameOffset}; 19 | m_frameOffset -= WORD_SIZE; 20 | return res; 21 | } 22 | 23 | assert(m_allocatedRegs < MAX_REGS); 24 | return InReg{m_tempMap.newTemp()}; 25 | }(formals[i]); 26 | m_formals.push_back(varAccess); 27 | } 28 | } 29 | 30 | temp::Label Frame::name() const { return m_name; } 31 | 32 | const AccessList &Frame::formals() const { return m_formals; } 33 | 34 | VariableAccess Frame::allocateLocal(bool escapes) { 35 | if (escapes) { 36 | auto res = InFrame{m_frameOffset}; 37 | m_frameOffset -= WORD_SIZE; 38 | return res; 39 | } 40 | 41 | return InReg{m_tempMap.newTemp()}; 42 | } 43 | 44 | ir::Statement Frame::procEntryExit1(const ir::Statement &body) const { 45 | return body; 46 | } 47 | 48 | assembly::Instructions 49 | Frame::procEntryExit3(const assembly::Instructions &body) const { 50 | return body; 51 | } 52 | 53 | } // namespace x64 54 | } // namespace frame 55 | } // namespace tiger 56 | -------------------------------------------------------------------------------- /Chapter09/x64/x64Frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Frame.h" 4 | #include "x64Registers.h" 5 | #include 6 | 7 | namespace tiger { 8 | namespace frame { 9 | namespace x64 { 10 | // implements 11 | // https://docs.microsoft.com/en-gb/cpp/build/overview-of-x64-calling-conventions 12 | class Frame final : public frame::Frame { 13 | public: 14 | static const int WORD_SIZE = 8; 15 | 16 | Frame(temp::Map &tempMap, const temp::Label &name, const BoolList &formals); 17 | 18 | // Inherited via Frame 19 | virtual temp::Label name() const override; 20 | 21 | virtual const AccessList &formals() const override; 22 | 23 | virtual VariableAccess allocateLocal(bool escapes) override; 24 | 25 | virtual ir::Statement 26 | procEntryExit1(const ir::Statement &body) const override; 27 | 28 | assembly::Instructions 29 | procEntryExit3(const assembly::Instructions &body) const override; 30 | 31 | private: 32 | temp::Label m_name; 33 | AccessList m_formals; 34 | int m_frameOffset = -WORD_SIZE; 35 | // The __fastcall convention uses registers for the first four arguments 36 | static const size_t MAX_REGS = 4; 37 | size_t m_allocatedRegs = 0; 38 | }; 39 | } // namespace x64 40 | } // namespace frame 41 | } // namespace tiger 42 | -------------------------------------------------------------------------------- /Chapter09/x64/x64Machine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Machine.h" 4 | #include "x64CallingConvention.h" 5 | #include "x64CodeGenerator.h" 6 | 7 | namespace tiger { 8 | namespace x64 { 9 | 10 | class Machine : public tiger::Machine { 11 | public: 12 | // Inherited via Machine 13 | virtual frame::CallingConvention &callingConvention() override; 14 | virtual const frame::CallingConvention &callingConvention() const override; 15 | virtual assembly::CodeGenerator &codeGenerator() override; 16 | virtual const assembly::CodeGenerator &codeGenerator() const override; 17 | 18 | private: 19 | frame::x64::CallingConvention m_callingConvention; 20 | assembly::x64::CodeGenerator m_codeGenerator{m_callingConvention}; 21 | 22 | // Inherited via Machine 23 | virtual temp::PredefinedRegisters predefinedRegisters() override; 24 | }; 25 | } // namespace x64 26 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter09/x64/x64Registers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TempMap.h" 3 | 4 | namespace tiger { 5 | namespace frame { 6 | namespace x64 { 7 | 8 | enum class Registers { 9 | RAX, 10 | RDX, 11 | RCX, 12 | RBX, 13 | RSI, 14 | RDI, 15 | RBP, 16 | RSP, 17 | R8, 18 | R9, 19 | R10, 20 | R11, 21 | R12, 22 | R13, 23 | R14, 24 | R15, 25 | RIP, 26 | MM0, 27 | MM1, 28 | MM2, 29 | MM3, 30 | MM4, 31 | MM5, 32 | MM6, 33 | MM7, 34 | FP0, 35 | FP1, 36 | FP2, 37 | FP3, 38 | FP4, 39 | FP5, 40 | FP6, 41 | FP7, 42 | XMM8, 43 | XMM9, 44 | XMM10, 45 | XMM11, 46 | XMM12, 47 | XMM13, 48 | XMM14, 49 | XMM15, 50 | XMM16, 51 | XMM17, 52 | XMM18, 53 | XMM19, 54 | XMM20, 55 | XMM21, 56 | XMM22, 57 | XMM23, 58 | XMM24, 59 | XMM25, 60 | XMM26, 61 | XMM27, 62 | XMM28, 63 | XMM29, 64 | XMM30, 65 | XMM31, 66 | Size 67 | }; 68 | 69 | constexpr temp::Register reg(Registers reg) { 70 | return temp::Register{static_cast(reg)}; 71 | } 72 | 73 | static_assert(static_cast(Registers::Size) <= temp::MIN_TEMP, 74 | "MIN_TEMP should be greater than the greatest register"); 75 | } // namespace x64 76 | } // namespace frame 77 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter10/CallingConvention.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Frame.h" 3 | 4 | namespace tiger { 5 | namespace assembly { 6 | class Instruction; 7 | using Instructions = std::vector; 8 | } // namespace assembly 9 | 10 | namespace frame { 11 | class CallingConvention { 12 | public: 13 | virtual ~CallingConvention() = default; 14 | 15 | virtual std::unique_ptr createFrame(temp::Map &tempMap, 16 | const temp::Label &name, 17 | const BoolList &formals) = 0; 18 | virtual int wordSize() const = 0; 19 | 20 | virtual temp::Register framePointer() const = 0; 21 | 22 | virtual temp::Register returnValue() const = 0; 23 | 24 | virtual temp::Register stackPointer() const = 0; 25 | 26 | virtual const temp::Registers &callDefinedRegisters() const = 0; 27 | 28 | virtual const temp::Registers &calleeSavedRegisters() const = 0; 29 | 30 | virtual const temp::Registers &argumentRegisters() const = 0; 31 | 32 | ir::Expression accessFrame(const VariableAccess &access, 33 | const ir::Expression &framePtr) const; 34 | 35 | ir::Expression externalCall(const temp::Label &name, 36 | const std::vector &args); 37 | 38 | temp::Registers liveAtExitRegisters() const; 39 | 40 | // calculate register liveness 41 | assembly::Instructions 42 | procEntryExit2(const assembly::Instructions &body) const; 43 | }; 44 | } // namespace frame 45 | } // namespace tiger 46 | -------------------------------------------------------------------------------- /Chapter10/ErrorHandler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace tiger { 8 | template class ErrorHandler { 9 | public: 10 | template struct result { typedef void type; }; 11 | 12 | template 13 | void operator()(Message const &message, What const &what, 14 | Iterator err_pos) const { 15 | class TigerError : public std::logic_error { 16 | using std::logic_error::logic_error; 17 | }; 18 | const auto &pos = err_pos.get_position(); 19 | std::stringstream sst; 20 | sst << pos.file << "(" << pos.line << "," << pos.column << "): " << message 21 | << ": " << what << '\n'; 22 | sst << "'"; 23 | std::copy(err_pos.get_currentline_begin(), err_pos.get_currentline_end(), 24 | std::ostream_iterator(sst)); 25 | sst << "'\n"; 26 | sst << std::setw(pos.column) << " " 27 | << "^- here\n"; 28 | throw TigerError{sst.str()}; 29 | } 30 | }; 31 | } // namespace tiger 32 | -------------------------------------------------------------------------------- /Chapter10/FlowGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TempRegister.h" 3 | #include "Assembly.h" 4 | #include 5 | #include 6 | 7 | namespace tiger { 8 | 9 | namespace regalloc { 10 | 11 | using LiveRegisters = std::vector; 12 | 13 | class FlowGraph 14 | : public boost::adjacency_list 16 | { 17 | public: 18 | FlowGraph(const assembly::Instructions &instructions); 19 | 20 | const temp::Registers &uses(vertex_descriptor v) const; 21 | const temp::Registers &defs(vertex_descriptor v) const; 22 | bool isMove(vertex_descriptor v) const; 23 | 24 | private: 25 | using base = boost::adjacency_list; 27 | LiveRegisters m_uses; 28 | LiveRegisters m_defs; 29 | boost::dynamic_bitset<> m_isMove; 30 | }; 31 | 32 | } // namespace regalloc 33 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter10/Fragment.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Tree.h" 3 | #include 4 | 5 | namespace tiger { 6 | 7 | namespace frame { 8 | class Frame; 9 | } 10 | 11 | struct StringFragment { 12 | temp::Label m_label; 13 | std::string m_string; 14 | }; 15 | 16 | struct FunctionFragment { 17 | ir::Statement m_body; 18 | std::shared_ptr m_frame; 19 | }; 20 | 21 | using Fragment = boost::variant; 22 | using FragmentList = std::vector; 23 | } // namespace tiger 24 | -------------------------------------------------------------------------------- /Chapter10/Frame.cpp: -------------------------------------------------------------------------------- 1 | #include "Frame.h" 2 | #include "Assembly.h" 3 | #include 4 | #include 5 | 6 | namespace tiger { 7 | namespace frame { 8 | 9 | Frame::Frame(temp::Map &tempMap, const CallingConvention &callingConvention) : 10 | m_tempMap{tempMap}, m_callingConvention{callingConvention} {} 11 | 12 | ir::Statement Frame::procEntryExit1(const ir::Statement &body) const { 13 | return ir::Sequence{ 14 | ranges::view::concat(m_parameterMoves, ranges::view::single(body))}; 15 | } 16 | 17 | assembly::Instructions 18 | Frame::procEntryExit3(const assembly::Instructions &body) const { 19 | return body; 20 | } 21 | 22 | } // namespace frame 23 | } // namespace tiger 24 | -------------------------------------------------------------------------------- /Chapter10/Frame.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TempMap.h" 3 | #include "Tree.h" 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tiger { 9 | 10 | namespace assembly { 11 | class Instruction; 12 | using Instructions = std::vector; 13 | } // namespace assembly 14 | 15 | namespace frame { 16 | 17 | class CallingConvention; 18 | 19 | using BoolList = boost::dynamic_bitset<>; 20 | 21 | struct InFrame { 22 | int m_offset; 23 | }; 24 | 25 | struct InReg { 26 | temp::Register m_reg; 27 | }; 28 | 29 | using VariableAccess = boost::variant; 30 | using AccessList = std::vector; 31 | 32 | class Frame { 33 | public: 34 | Frame(temp::Map &tempMap, const CallingConvention &callingConvention); 35 | 36 | virtual ~Frame() noexcept = default; 37 | 38 | virtual temp::Label name() const = 0; 39 | virtual const AccessList &formals() const = 0; 40 | virtual VariableAccess allocateLocal(bool escapes) = 0; 41 | // move input parameters to the function frame 42 | // store and restore callee saved registers 43 | ir::Statement procEntryExit1(const ir::Statement &body) const; 44 | // add prolog and epilog 45 | assembly::Instructions 46 | procEntryExit3(const assembly::Instructions &body) const; 47 | 48 | protected: 49 | 50 | temp::Map &m_tempMap; 51 | const CallingConvention &m_callingConvention; 52 | ir::Statements m_parameterMoves; 53 | }; 54 | 55 | } // namespace frame 56 | } // namespace tiger 57 | -------------------------------------------------------------------------------- /Chapter10/Machine.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TempMap.h" 3 | #include 4 | 5 | namespace tiger { 6 | namespace frame { 7 | class CallingConvention; 8 | } 9 | 10 | namespace assembly { 11 | class CodeGenerator; 12 | } // namespace assembly 13 | 14 | class Machine { 15 | public: 16 | virtual ~Machine() = default; 17 | 18 | virtual temp::PredefinedRegisters predefinedRegisters() const = 0; 19 | 20 | virtual frame::CallingConvention &callingConvention() = 0; 21 | virtual const frame::CallingConvention &callingConvention() const = 0; 22 | 23 | virtual assembly::CodeGenerator &codeGenerator() = 0; 24 | virtual const assembly::CodeGenerator &codeGenerator() const = 0; 25 | }; 26 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter10/MachineRegistrar.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Machine.h" 3 | #include 4 | #include 5 | #include 6 | 7 | #define REGISTER_MACHINE(name, type) \ 8 | \ 9 | namespace { \ 10 | tiger::AutoRegistrar registered_##name(#name); \ 11 | } 12 | 13 | namespace tiger { 14 | 15 | using MachineRegistrar = 16 | std::unordered_map()>>; 17 | 18 | inline MachineRegistrar &machineRegistrar() { 19 | static MachineRegistrar registrar; 20 | return registrar; 21 | } 22 | 23 | struct NoSuchArchError : std::logic_error { 24 | using std::logic_error::logic_error; 25 | }; 26 | 27 | template 28 | inline void registerMachine(const std::string &arch, Creator &&creator) { 29 | machineRegistrar()[arch] = std::forward(creator); 30 | } 31 | 32 | template struct AutoRegistrar { 33 | AutoRegistrar(const std::string &arch) { 34 | registerMachine(arch, []() { return std::make_unique(); }); 35 | } 36 | }; 37 | 38 | inline std::unique_ptr createMachine(const std::string &arch) { 39 | auto it = machineRegistrar().find(arch); 40 | if (it == machineRegistrar().end()) { 41 | throw NoSuchArchError{"no machine named " + arch}; 42 | } 43 | return it->second(); 44 | } 45 | 46 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter10/MachineRegistration.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "MachineRegistrar.h" 3 | @MACHINE_INCLUDES@ 4 | 5 | @MACHINE_REGISTRATIONS@ -------------------------------------------------------------------------------- /Chapter10/Program.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "TempRegister.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace tiger { 11 | 12 | struct CompileResults { 13 | std::string m_assembly; 14 | using InterferenceGraph = std::map>; 15 | std::vector m_interferenceGraphs; 16 | 17 | friend std::ostream &operator<<(std::ostream &ost, 18 | const CompileResults &results); 19 | }; 20 | 21 | using CompileResult = boost::optional; 22 | 23 | CompileResult compileFile(const std::string &arch, const std::string &filename); 24 | 25 | CompileResult compile(const std::string &arch, const std::string &string); 26 | 27 | } // namespace tiger 28 | -------------------------------------------------------------------------------- /Chapter10/Skipper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "warning_suppress.h" 3 | MSC_DIAG_OFF(4996 4459) 4 | #include 5 | MSC_DIAG_ON() 6 | 7 | namespace tiger { 8 | template 9 | class Skipper : public boost::spirit::qi::grammar { 10 | public: 11 | Skipper() : Skipper::base_type(skip) { 12 | namespace ascii = boost::spirit::ascii; 13 | 14 | using ascii::char_; 15 | ascii::space_type space; 16 | 17 | skip = space // tab/space/cr/lf 18 | | "/*" >> *(char_ - "*/") >> "*/" // comments 19 | ; 20 | } 21 | 22 | private: 23 | boost::spirit::qi::rule skip; 24 | }; 25 | } // namespace tiger -------------------------------------------------------------------------------- /Chapter10/TempLabel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace tiger { 6 | namespace temp { 7 | 8 | // a label is just a string 9 | struct Label : type_safe::strong_typedef, 10 | type_safe::strong_typedef_op::equality_comparison