IT OUTPUTS OBJECT FILES!!!!!!!!!!!!!!!!!!!!!!!!!!
This commit is contained in:
parent
322a87ef29
commit
3f70d4b8b6
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ struct Arguments
|
||||
static std::string output_fname;
|
||||
|
||||
static bool wimport;
|
||||
static bool emit_llvm;
|
||||
|
||||
static std::string cpu;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -19,5 +19,7 @@ class IRBuilder
|
||||
|
||||
llvm::IRBuilder<>* getBuilder();
|
||||
|
||||
std::string getGeneratedIR();
|
||||
void resolveToLLVMIR(std::string path);
|
||||
|
||||
void resolveToObjectFile(std::string path);
|
||||
};
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
@ -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
60
test.py
@ -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:
|
||||
|
@ -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": ""}}
|
@ -1 +1 @@
|
||||
1 + 3 * 5
|
||||
1 + 3 * 5
|
@ -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"}}
|
Loading…
Reference in New Issue
Block a user