IT OUTPUTS OBJECT FILES!!!!!!!!!!!!!!!!!!!!!!!!!!

This commit is contained in:
apio 2022-08-24 16:24:32 +02:00
parent 322a87ef29
commit 3f70d4b8b6
14 changed files with 181 additions and 27 deletions

View File

@ -66,9 +66,8 @@ target_include_directories(sapphirec PUBLIC src/external/tclap-1.2.5/include)
target_include_directories(sapphirec PUBLIC src/external)
target_precompile_headers(sapphirec PUBLIC src/sapphirepch.h)
llvm_map_components_to_libnames(llvm_libs support core irreader)
llvm_map_components_to_libnames(llvm_libs all core support irreader x86asmparser x86codegen x86desc x86disassembler x86info x86targetmca aarch64asmparser aarch64codegen aarch64desc aarch64disassembler aarch64info aarch64utils)
# Link against LLVM libraries
target_link_libraries(sapphirec ${llvm_libs})
install(TARGETS sapphirec)

View File

@ -9,6 +9,7 @@ std::string Arguments::output_fname;
bool Arguments::wimport;
llvm::Triple Arguments::TargetTriple;
std::string Arguments::cpu;
bool Arguments::emit_llvm;
void Arguments::parse(int argc, char** argv)
{
@ -17,20 +18,28 @@ void Arguments::parse(int argc, char** argv)
{
TCLAP::CmdLine command_line("The Sapphire compiler.", ' ', "0.1");
TCLAP::UnlabeledValueArg<std::string> input_fname_arg("file", "Input file.", true, "test.sp", "string");
TCLAP::UnlabeledValueArg<std::string> input_fname_arg("file", "Input file.", true, "program.sp", "string");
TCLAP::ValueArg<std::string> output_fname_arg("o", "output", "Output file.", false, "sp-output", "string");
TCLAP::ValueArg<std::string> output_fname_arg("o", "output", "Output file.", false, "output.o", "string");
TCLAP::ValueArg<std::string> march_arg("", "march", "Architecture to compile for.", false, "native", "string");
TCLAP::ValueArg<std::string> mcpu_arg("", "mcpu", "CPU to compile for.", false, "generic", "string");
TCLAP::ValueArg<std::string> msystem_arg("", "msystem", "Operating System to compile for.", false, "native",
"string");
TCLAP::SwitchArg emit_llvm_arg("", "emit-llvm", "Emit LLVM IR instead of an object file.");
#ifndef NO_BENCHMARKING
TCLAP::SwitchArg mprofile_arg("", "mprofile", "Show execution times for functions.");
#endif
TCLAP::SwitchArg wimport_arg("", "wimport", "Show a warning when trying to import an already imported file.");
command_line.add(wimport_arg);
command_line.add(emit_llvm_arg);
#ifndef NO_BENCHMARKING
command_line.add(mprofile_arg);
#endif
command_line.add(input_fname_arg);
command_line.add(output_fname_arg);
@ -44,12 +53,15 @@ void Arguments::parse(int argc, char** argv)
input_fname = input_fname_arg.getValue();
output_fname = output_fname_arg.getValue();
wimport = wimport_arg.getValue();
emit_llvm = emit_llvm_arg.getValue();
cpu = mcpu_arg.getValue();
setTriple(march_arg.getValue(), msystem_arg.getValue());
#ifndef NO_BENCHMARKING
if (mprofile_arg.getValue()) __benchmark_impl::enable();
#endif
}
catch (TCLAP::ArgException& e)
{
@ -68,10 +80,11 @@ void Arguments::setTriple(const std::string& arch, const std::string& system)
}
if (system != "native")
{
targetTriple.setOSAndEnvironmentName(system);
targetTriple.setOSName(system);
}
targetTriple.setVendor(llvm::Triple::VendorType::UnknownVendor); // let's leave it like that
targetTriple.setEnvironment(llvm::Triple::EnvironmentType::UnknownEnvironment);
TargetTriple = targetTriple;
}

View File

@ -10,6 +10,7 @@ struct Arguments
static std::string output_fname;
static bool wimport;
static bool emit_llvm;
static std::string cpu;

View File

@ -85,6 +85,18 @@ void Error::show_import_lines(const Location& loc, void (*import_line_printer)(c
exit(1);
}
void Error::throw_warning_without_location(const std::string& details)
{
std::cerr << "\033[1;1m";
std::cerr << "\033[33;49m";
std::cerr << "warning: ";
std::cerr << "\033[0;0m";
std::cerr << details;
std::cerr << std::endl;
}
void Error::throw_warning(const Location& loc, const std::string line_text, const std::string& details)
{
show_import_lines(loc, show_import_line, std::cout);

View File

@ -11,6 +11,8 @@ void show_import_line(const Location& loc, std::ostream& output_stream);
void throw_warning(const Location& loc, const std::string line_text, const std::string& details);
void throw_warning_without_location(const std::string& details);
void show_import_lines(const Location& loc, void (*import_line_printer)(const Location&, std::ostream&),
std::ostream& stream);
} // namespace Error

View File

@ -1,8 +1,17 @@
#include "IRBuilder.h"
#include "Arguments.h"
#include "Error.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Value.h"
#include "llvm/IR/Verifier.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/CodeGen.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
IRBuilder::IRBuilder()
{
@ -31,12 +40,76 @@ void IRBuilder::create_main_function(std::shared_ptr<ASTNode> expression)
llvm::verifyFunction(*main);
}
std::string IRBuilder::getGeneratedIR()
void IRBuilder::resolveToLLVMIR(std::string path)
{
std::string str;
llvm::raw_string_ostream oss(str);
std::error_code EC;
llvm::raw_fd_ostream dest(path, EC, llvm::sys::fs::OF_None);
module->print(oss, nullptr);
if (EC)
{
Error::throw_error_without_location("Could not open file " + Arguments::output_fname + " :" + EC.message());
}
return oss.str();
}
module->print(dest, nullptr);
dest.flush();
}
void IRBuilder::resolveToObjectFile(std::string path)
{
// edit this section when adding support for more architectures
LLVMInitializeX86TargetInfo();
LLVMInitializeAArch64TargetInfo();
LLVMInitializeX86Target();
LLVMInitializeAArch64Target();
LLVMInitializeX86TargetMC();
LLVMInitializeAArch64TargetMC();
LLVMInitializeX86AsmParser();
LLVMInitializeAArch64AsmParser();
LLVMInitializeX86AsmPrinter();
LLVMInitializeAArch64AsmPrinter();
std::string TargetTriple = Arguments::TargetTriple.getTriple();
module->setTargetTriple(TargetTriple);
std::string err;
auto Target = llvm::TargetRegistry::lookupTarget(TargetTriple, err);
if (!Target)
{
Error::throw_error_without_location(err);
}
std::string CPU;
if (Arguments::cpu == "native")
{
CPU = llvm::sys::getHostCPUName();
Error::throw_warning_without_location("Using host CPU: " + CPU);
}
else
CPU = Arguments::cpu;
auto Features = "";
llvm::TargetOptions opt;
auto RM = llvm::Optional<llvm::Reloc::Model>();
auto TheTargetMachine = Target->createTargetMachine(TargetTriple, CPU, Features, opt, RM);
module->setDataLayout(TheTargetMachine->createDataLayout());
std::error_code EC;
llvm::raw_fd_ostream dest(path, EC, llvm::sys::fs::OF_None);
if (EC)
{
Error::throw_error_without_location("Could not open file " + Arguments::output_fname + " :" + EC.message());
}
llvm::legacy::PassManager pass;
auto FileType = llvm::CGFT_ObjectFile;
if (TheTargetMachine->addPassesToEmitFile(pass, dest, nullptr, FileType))
{
Error::throw_error_without_location("Cannot emit object file");
}
pass.run(*module);
dest.flush();
}

View File

@ -19,5 +19,7 @@ class IRBuilder
llvm::IRBuilder<>* getBuilder();
std::string getGeneratedIR();
void resolveToLLVMIR(std::string path);
void resolveToObjectFile(std::string path);
};

View File

@ -48,5 +48,7 @@ int main(int argc, char** argv)
builder.create_main_function(ast);
}
std::cout << builder.getGeneratedIR();
if (Arguments::emit_llvm) builder.resolveToLLVMIR(Arguments::output_fname);
else
builder.resolveToObjectFile(Arguments::output_fname);
}

View File

@ -23,6 +23,8 @@ std::string to_string(const float& value)
return result.str();
}
#ifndef NO_BENCHMARKING
__benchmark_impl::__benchmark_impl(std::string_view __function_name) : m_FunctionName(__function_name)
{
m_InternalBenchmarkingEnabled = m_BenchmarkingEnabled;
@ -59,4 +61,6 @@ void __benchmark_impl::enable()
m_BenchmarkingEnabled = true;
}
bool __benchmark_impl::m_BenchmarkingEnabled;
bool __benchmark_impl::m_BenchmarkingEnabled;
#endif

View File

@ -25,6 +25,7 @@ std::string to_string(const int& value);
std::string to_string(const float& value);
/* Benchmarking utilities. */
#ifndef NO_BENCHMARKING
class __benchmark_impl
{
@ -44,4 +45,7 @@ class __benchmark_impl
bool m_InternalBenchmarkingEnabled;
};
#define benchmark(message) __benchmark_impl __benchmark_impl_timer(message)
#define benchmark(message) __benchmark_impl __benchmark_impl_timer(message)
#else
#define benchmark(message)
#endif

60
test.py
View File

@ -2,8 +2,8 @@
import subprocess
import json
import os
import argparse
import sys
import shutil
def load_test_case(filename: str) -> dict:
@ -23,9 +23,9 @@ def test_test_case(test_case: dict) -> bool:
compiler_stdout = compiler["stdout"]
compiler_stderr = compiler["stderr"]
command = " ".join(["build/sapphirec", filepath, "-o",
".tests-bin/exe"] + extra_flags)
".tests-bin/output.o"] + extra_flags)
print(f"-> Running command: {command}")
compile_task = subprocess.Popen(["build/sapphirec", filepath, "-o", ".tests-bin/exe"] + extra_flags,
compile_task = subprocess.Popen(["build/sapphirec", filepath, "-o", ".tests-bin/output.o"] + extra_flags,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
retcode = compile_task.wait()
retstdout = compile_task.stdout.read().decode('utf-8')
@ -42,6 +42,33 @@ def test_test_case(test_case: dict) -> bool:
print("-> Test failed: compiler stderr does not match test case")
print(retstderr)
return False
runtime = test_case.get("run", False)
if runtime is not False:
print("-> Running command: gcc .tests-bin/output.o -o .tests-bin/output")
link_task = subprocess.Popen(["gcc", ".tests-bin/output.o", "-o", ".tests-bin/output"] + extra_flags,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if link_task.wait() != 0:
print(f"-> Failed to link program")
return False
print(f"-> Running command: .tests-bin/output")
runtime_task = subprocess.Popen([".tests-bin/output"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
runtime_code = runtime_task.wait()
runtime_stdout = runtime_task.stdout.read().decode('utf-8')
runtime_stderr = runtime_task.stderr.read().decode('utf-8')
test_code = runtime["exit-code"]
if runtime_code != runtime["exit-code"]:
print(
f"-> Test failed: program exited with code {str(runtime_code)} (expected {str(test_code)})")
return False
if runtime_stdout != runtime["stdout"]:
print("-> Test failed: program stdout does not match test case")
print(retstdout)
return False
if runtime_stderr != runtime["stderr"]:
print("-> Test failed: program stderr does not match test case")
print(retstderr)
return False
print("-> Test succeeded")
return True
@ -50,7 +77,7 @@ test_cases = []
def run_tests():
os.mkdir(".tests-bin")
os.makedirs(".tests-bin", exist_ok=True)
for filename in os.listdir("tests"):
if filename.endswith(".json"):
test_cases.append(load_test_case(os.path.join("tests", filename)))
@ -60,21 +87,21 @@ def run_tests():
for test_case in test_cases:
if not test_test_case(test_case):
print("-> Test suite failed")
os.rmdir(".tests-bin")
shutil.rmtree(".tests-bin")
sys.exit(1)
else:
print("-> Test suite succeeded")
os.rmdir(".tests-bin")
shutil.rmtree(".tests-bin")
def create_test(filename: str, extra_flags: list):
os.mkdir(".tests-bin")
os.makedirs(".tests-bin", exist_ok=True)
test_case = {}
test_case["file"] = filename
filepath = os.path.join("tests", filename)
compiler = {}
compiler["flags"] = extra_flags
compile_task = subprocess.Popen(["build/sapphirec", filepath, "-o", ".tests-bin/exe"] + extra_flags,
compile_task = subprocess.Popen(["build/sapphirec", filepath, "-o", ".tests-bin/output.o"] + extra_flags,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
retcode = compile_task.wait()
retstdout = compile_task.stdout.read().decode('utf-8')
@ -83,11 +110,26 @@ def create_test(filename: str, extra_flags: list):
compiler["stdout"] = retstdout
compiler["stderr"] = retstderr
test_case["compile"] = compiler
if retcode == 0:
link_task = subprocess.Popen(["gcc", ".tests-bin/output.o", "-o", ".tests-bin/output"] + extra_flags,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
link_status = link_task.wait()
if link_status == 0:
program = {}
runtime_task = subprocess.Popen([".tests-bin/output"],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
runtime_code = runtime_task.wait()
runtime_stdout = runtime_task.stdout.read().decode('utf-8')
runtime_stderr = runtime_task.stderr.read().decode('utf-8')
program["exit-code"] = runtime_code
program["stdout"] = runtime_stdout
program["stderr"] = runtime_stderr
test_case["run"] = program
ofilepath = ".".join(filepath.split(".")[:-1]) + ".json"
ofile = open(ofilepath, "w+")
json.dump(test_case, ofile)
ofile.close()
os.rmdir(".tests-bin")
shutil.rmtree(".tests-bin")
if len(sys.argv) < 2:

View File

@ -1 +1 @@
{"file": "calc.sp", "compile": {"flags": [], "exit-code": 0, "stdout": "; ModuleID = 'tests/calc.sp'\nsource_filename = \"tests/calc.sp\"\n\ndefine i32 @main() {\nentry:\n ret i32 16\n}\n", "stderr": ""}}
{"file": "calc.sp", "compile": {"flags": [], "exit-code": 0, "stdout": "", "stderr": ""}, "run": {"exit-code": 16, "stdout": "", "stderr": ""}}

View File

@ -1 +1 @@
1 + 3 * 5
1 + 3 * 5

View File

@ -1 +1 @@
{"file": "wimport.sp", "compile": {"flags": [], "exit-code": 1, "stdout": "", "stderr": "\u001b[1;1mtests/wimport.sp:1:22: \u001b[31;49merror: \u001b[0;0mexpected a number\n1 \n \u001b[31;49m^\u001b[0;0m\n"}}
{"file": "wimport.sp", "compile": {"flags": ["--wimport"], "exit-code": 1, "stdout": "\u001b[1;1mtests/wimport.sp:1:8: \u001b[33;49mwarning: \u001b[0;0mfile already imported, skipping\n1 import tests/wimport;\n \u001b[33;49m^\u001b[0;0m\n", "stderr": "\u001b[1;1mtests/wimport.sp:1:22: \u001b[31;49merror: \u001b[0;0mexpected a number\n1 \n \u001b[31;49m^\u001b[0;0m\n"}}