rafalcieslak / ccoscope Goto Github PK
View Code? Open in Web Editor NEWCompiler Construction, mixed with a custom language and LLVM Kaleidoscope, brewed together and still not working.
Compiler Construction, mixed with a custom language and LLVM Kaleidoscope, brewed together and still not working.
As of 01917e4 , when we try to compile it, we get wall of errors:
In file included from /usr/include/c++/4.9/functional:55:0,
from /usr/include/c++/4.9/memory:79,
from /home/ja/llvm/include/llvm/ADT/SmallVector.h:29,
from /home/ja/llvm/include/llvm/ADT/ArrayRef.h:14,
from /home/ja/llvm/include/llvm/IR/IRBuilder.h:18,
from /home/ja/Studies/7sem/cc/project/CCoscope/src/codegencontext.h:5,
from /home/ja/Studies/7sem/cc/project/CCoscope/src/codegencontext.cpp:1:
/usr/include/c++/4.9/tuple: In instantiation of ‘constexpr std::_Head_base<_Idx, _Head, false>::_Head_base(_UHead&&) [with _UHead = ccoscope::Proxy<ccoscope::IntegerTypeAST>; long unsigned int _Idx = 2ul; _Head = ccoscope::Proxy<ccoscope::TypeAST>]’:
/usr/include/c++/4.9/tuple:285:57: recursively required from ‘constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(std::_Tuple_impl<_Idx, _UHead, _UTails ...>&&) [with _UHead = ccoscope::Proxy<ccoscope::IntegerTypeAST>; _UTails = {ccoscope::Proxy<ccoscope::IntegerTypeAST>}; long unsigned int _Idx = 1ul; _Head = ccoscope::Proxy<ccoscope::TypeAST>; _Tail = {ccoscope::Proxy<ccoscope::TypeAST>}]’
/usr/include/c++/4.9/tuple:285:57: required from ‘constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(std::_Tuple_impl<_Idx, _UHead, _UTails ...>&&) [with _UHead = const char*; _UTails = {ccoscope::Proxy<ccoscope::IntegerTypeAST>, ccoscope::Proxy<ccoscope::IntegerTypeAST>}; long unsigned int _Idx = 0ul; _Head = std::basic_string<char>; _Tail = {ccoscope::Proxy<ccoscope::TypeAST>, ccoscope::Proxy<ccoscope::TypeAST>}]’
/usr/include/c++/4.9/tuple:424:72: required from ‘constexpr std::tuple< <template-parameter-1-1> >::tuple(std::tuple<_Args1 ...>&&) [with _UElements = {const char*, ccoscope::Proxy<ccoscope::IntegerTypeAST>, ccoscope::Proxy<ccoscope::IntegerTypeAST>}; <template-parameter-2-2> = void; _Elements = {std::basic_string<char, std::char_traits<char>, std::allocator<char> >, ccoscope::Proxy<ccoscope::TypeAST>, ccoscope::Proxy<ccoscope::TypeAST>}]’
/home/ja/Studies/7sem/cc/project/CCoscope/src/codegencontext.cpp:22:72: required from here
/usr/include/c++/4.9/tuple:142:42: error: call of overloaded ‘Proxy(ccoscope::Proxy<ccoscope::IntegerTypeAST>)’ is ambiguous
: _M_head_impl(std::forward<_UHead>(__h)) { }
^
/usr/include/c++/4.9/tuple:142:42: note: candidates are:
In file included from /home/ja/Studies/7sem/cc/project/CCoscope/src/types.h:15:0,
from /home/ja/Studies/7sem/cc/project/CCoscope/src/tree.h:6,
from /home/ja/Studies/7sem/cc/project/CCoscope/src/codegencontext.h:13,
from /home/ja/Studies/7sem/cc/project/CCoscope/src/codegencontext.cpp:1:
/home/ja/Studies/7sem/cc/project/CCoscope/src/proxy.h:24:5: note: ccoscope::Proxy<T>::Proxy(const T*) [with T = ccoscope::TypeAST]
Proxy(const T* node)
^
/home/ja/Studies/7sem/cc/project/CCoscope/src/proxy.h:16:7: note: constexpr ccoscope::Proxy<ccoscope::TypeAST>::Proxy(const ccoscope::Proxy<ccoscope::TypeAST>&)
class Proxy {
^
In file included from /usr/include/c++/4.9/functional:55:0,
from /usr/include/c++/4.9/memory:79,
from /home/ja/llvm/include/llvm/ADT/SmallVector.h:29,
from /home/ja/llvm/include/llvm/ADT/ArrayRef.h:14,
from /home/ja/llvm/include/llvm/IR/IRBuilder.h:18,
from /home/ja/Studies/7sem/cc/project/CCoscope/src/codegencontext.h:5,
from /home/ja/Studies/7sem/cc/project/CCoscope/src/codegencontext.cpp:1:
/usr/include/c++/4.9/tuple: In instantiation of ‘constexpr std::_Head_base<_Idx, _Head, false>::_Head_base(_UHead&&) [with _UHead = ccoscope::Proxy<ccoscope::IntegerTypeAST>; long unsigned int _Idx = 1ul; _Head = ccoscope::Proxy<ccoscope::TypeAST>]’:
/usr/include/c++/4.9/tuple:285:57: recursively required from ‘constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(std::_Tuple_impl<_Idx, _UHead, _UTails ...>&&) [with _UHead = ccoscope::Proxy<ccoscope::IntegerTypeAST>; _UTails = {ccoscope::Proxy<ccoscope::IntegerTypeAST>}; long unsigned int _Idx = 1ul; _Head = ccoscope::Proxy<ccoscope::TypeAST>; _Tail = {ccoscope::Proxy<ccoscope::TypeAST>}]’
/usr/include/c++/4.9/tuple:285:57: required from ‘constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(std::_Tuple_impl<_Idx, _UHead, _UTails ...>&&) [with _UHead = const char*; _UTails = {ccoscope::Proxy<ccoscope::IntegerTypeAST>, ccoscope::Proxy<ccoscope::IntegerTypeAST>}; long unsigned int _Idx = 0ul; _Head = std::basic_string<char>; _Tail = {ccoscope::Proxy<ccoscope::TypeAST>, ccoscope::Proxy<ccoscope::TypeAST>}]’
/usr/include/c++/4.9/tuple:424:72: required from ‘constexpr std::tuple< <template-parameter-1-1> >::tuple(std::tuple<_Args1 ...>&&) [with _UElements = {const char*, ccoscope::Proxy<ccoscope::IntegerTypeAST>, ccoscope::Proxy<ccoscope::IntegerTypeAST>}; <template-parameter-2-2> = void; _Elements = {std::basic_string<char, std::char_traits<char>, std::allocator<char> >, ccoscope::Proxy<ccoscope::TypeAST>, ccoscope::Proxy<ccoscope::TypeAST>}]’
/home/ja/Studies/7sem/cc/project/CCoscope/src/codegencontext.cpp:22:72: required from here
/usr/include/c++/4.9/tuple:142:42: error: call of overloaded ‘Proxy(ccoscope::Proxy<ccoscope::IntegerTypeAST>)’ is ambiguous
: _M_head_impl(std::forward<_UHead>(__h)) { }
^
/usr/include/c++/4.9/tuple:142:42: note: candidates are:
In file included from /home/ja/Studies/7sem/cc/project/CCoscope/src/types.h:15:0,
from /home/ja/Studies/7sem/cc/project/CCoscope/src/tree.h:6,
from /home/ja/Studies/7sem/cc/project/CCoscope/src/codegencontext.h:13,
from /home/ja/Studies/7sem/cc/project/CCoscope/src/codegencontext.cpp:1:
/home/ja/Studies/7sem/cc/project/CCoscope/src/proxy.h:24:5: note: ccoscope::Proxy<T>::Proxy(const T*) [with T = ccoscope::TypeAST]
Proxy(const T* node)
^
/home/ja/Studies/7sem/cc/project/CCoscope/src/proxy.h:16:7: note: constexpr ccoscope::Proxy<ccoscope::TypeAST>::Proxy(const ccoscope::Proxy<ccoscope::TypeAST>&)
class Proxy {
^
....................
..................
As I understand, the problem here is that in proxy.h
we have template<class U> operator Proxy<U>()
and this implicit cast operator causes ambiguity when Type
(i.e. Proxy<TypeAST>
) is put as a key into std::map
. I could remove this operator but then it's a PITA throughout all the rest of the code, because everywhere where we return some type (e.g. FunctionType
) and have declared the return type to be Type
, we must manually invoke conversion.
How could we eat cake and have cake? Maybe some custom map
instead of the one from std::
?
When trying to define any function in tree.h
file, e.g. int foo(int x) { return x+1; }
, during compilation, we get errors -- in this case, it reads:
CMakeFiles/CCoscope.dir/tokenizer.cpp.o: In function
foo(int)': /home/ja/Studies/7sem/cc/project/CCoscope/src/tree.h:38: multiple definition of
foo(int)'
CMakeFiles/CCoscope.dir/compiler.cpp.o:/home/ja/Studies/7sem/cc/project/CCoscope/src/tree.h:38: first defined here
CMakeFiles/CCoscope.dir/tree.cpp.o: In functionfoo(int)': /home/ja/Studies/7sem/cc/project/CCoscope/src/tree.h:38: multiple definition of
foo(int)'
CMakeFiles/CCoscope.dir/compiler.cpp.o:/home/ja/Studies/7sem/cc/project/CCoscope/src/tree.h:38: first defined here
CMakeFiles/CCoscope.dir/lexer.cpp.o: In functionfoo(int)': /home/ja/Studies/7sem/cc/project/CCoscope/src/tree.h:38: multiple definition of
foo(int)'
CMakeFiles/CCoscope.dir/compiler.cpp.o:/home/ja/Studies/7sem/cc/project/CCoscope/src/tree.h:38: first defined here
CMakeFiles/CCoscope.dir/token.cpp.o: In functionstd::iterator_traits<std::_List_const_iterator<int> >::iterator_category std::__iterator_category<std::_List_const_iterator<int> >(std::_List_const_iterator<int> const&)': /home/ja/Studies/7sem/cc/project/CCoscope/src/tree.h:38: multiple definition of
foo(int)'
CMakeFiles/CCoscope.dir/compiler.cpp.o:/home/ja/Studies/7sem/cc/project/CCoscope/src/tree.h:38: first defined here
CMakeFiles/CCoscope.dir/parser.cpp.o: In functionfoo(int)': /home/ja/Studies/7sem/cc/project/CCoscope/src/tree.h:38: multiple definition of
foo(int)'
CMakeFiles/CCoscope.dir/compiler.cpp.o:/home/ja/Studies/7sem/cc/project/CCoscope/src/tree.h:38: first defined here
CMakeFiles/CCoscope.dir/utils.cpp.o: In functionGetTmpFile(std::string)': /home/ja/Studies/7sem/cc/project/CCoscope/src/utils.cpp:19: warning: the use of
tmpnam' is dangerous, better use `mkstemp'
collect2: error: ld returned 1 exit status
make[2]: *** [src/CCoscope] Błąd 1
make[1]: *** [src/CMakeFiles/CCoscope.dir/all] Błąd 2
make: *** [all] Błąd 2
Naturally, I don't really need this particular foo
function, but it just serves as an example.
We should support at least these command-line arguments:
filename.cco
- input file, or a list of input files. .cco files need compiling, .ll files need assembling, .o files need linking.-o
- output file name-c
- compile & assemble only, no linking.-S
- compile only, no assembling. This would emit llvm ir to the output file, and so it would no longer have to be displayed on the standard output.-On
- optimisation control. Simple to implement and may be useful for debugging.-l
- for passing libs to the linker. This will enable us to implement OpenGL support.-L
- lib dirs.What else could be useful?
I have an idea how to kill two birds with one stone.
We will need two things:
I think that nobody likes doing 1) nor 2). But if we combine them into one thing, namely, interpreter, it should even be fun. Semantics is just describing behaviour and for that the interpreter would be perfect. And we would keep writing examples in .cco
, then run them both on the interpreter and our compiled code and automatically compare results.
Does that sound good?
Refers to typematcher
branch.
Implement built-in Complex
type, i.e. a thing with double real part and double imaginary part, along with reasonable conversions and operations on it.
Refers to branch typematcher
, and the issue itself refers to the comment #20 (comment) .
Quick diagnosis -- we have mixed these two activities of our compiler which is probably not healthy :)
As of e34675e , when we look at the LLVM's IR our compiler produces on examples/pow.cco
, we get
@0 = private unnamed_addr constant [4 x i8] c"%f\0A\00"
@1 = private unnamed_addr constant [4 x i8] c"%f\0A\00"
@2 = private unnamed_addr constant [4 x i8] c"%f\0A\00"
@3 = private unnamed_addr constant [4 x i8] c"%d\0A\00"
@4 = private unnamed_addr constant [4 x i8] c"%d\0A\00"
declare i32 @putchar(i32)
declare i32 @printf(i8*, ...)
declare double @pow(double, double)
define i32 @main() {
entry:
%c_addr = alloca double
%b_addr = alloca double
%a_addr = alloca double
store double 0.000000e+00, double* %a_addr
store double 0.000000e+00, double* %b_addr
store double 0.000000e+00, double* %c_addr
store double 3.000000e+00, double* %a_addr
store double 2.000000e+00, double* %b_addr
%a = load double, double* %a_addr
%b = load double, double* %b_addr
%calltmp = call double @pow(double %a, double %b)
store double %calltmp, double* %c_addr
%a1 = load double, double* %a_addr
%calltmp2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i32 0, i32 0), double %a1)
%b3 = load double, double* %b_addr
%calltmp4 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @1, i32 0, i32 0), double %b3)
%c = load double, double* %c_addr
%calltmp5 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @2, i32 0, i32 0), double %c)
%a6 = load double, double* %a_addr
%b7 = load double, double* %b_addr
%calltmp8 = call double @pow(double %a6, double %b7)
%calltmp9 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @3, i32 0, i32 0), double %calltmp8)
%calltmp10 = call double @pow(double 3.000000e+00, double 2.000000e+00)
%calltmp11 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @4, i32 0, i32 0), double %calltmp10)
ret i32 0
}
The problem is that the last two calls to printf
get the %d
format specifier, and as a result those calls print rubbish. This must probably be the issue involving maintype
member function in AST class -- which is basically stubbed out, but I believe it should produce correct results in current state of implementation. The ad-hoc solution in CallExprAST::codegen
doesn't work unfortunately.
As a side note -- how about throwing away all of our type enums and type classes and use type system that LLVM provides? It should be safer and maybe problems will magically disappear.
It seems that something goes wrong when lexing (or parsing?) floating point numbers. On examples/types.cco
we get:
Syntax error, unexpected token LITERAL_INT( -4 ), line 7, column 11.
and the number is 3e-4
.
Similarly, 3.14
doesn't work either.
In the header of compiler.cpp
:
#include "codegencontext.h"
#include "codegencontext.cpp" // gcc needs it for some reason...
Why exactly is the .cpp
file included? That doesn't seem right. In fact, it causes CodegenContext::CodegenContext(...)
to be compiled twice, into compiler.o
and then codegencontext.o
, and thus the linker fails, as there are multiple definitions of that constructor available. Removing that line fixes the issue i.e. the sources compile successfully.
Can we remove that inclusion? Why was it needed in the first place?
It would be nice to have ::
operator, just as in C++.
This should be straightforward. You will need to add the for
keyword to the lexer grammar, a new class representing for
expressions in the AST, a grammar rule that will build the AST node instance when the keyword is spotted, and a codegen rule, which will be similar to if
and while
statements.
I think it would make sense to have return
as an enum in KeywordAST
instead of having separate ReturnExprAST
.
Who doesn't agree? :)
Currently the optimizer pass set is copied from Kaleidoscope. We should probably pick a larger set of passes.
We should specify what are values in our language and how they should behave. In particular, specify:
primitive
and compound
(e.g. values of type int
versus string
) and what it implies for the userMaybe some other points I forgot to mention?
I don't understand what exactly is going on in common/common_types.h
-- why are we taking care of whether this unit is compiled by C or C++ compiler when defining struct __cco_complex
, and then blissfully use templates below? It seems to me that, because we are using magic from LLVM like TypeBuilder
, we are convicted to using C++ only. Is that correct?
At the very least, type system should support:
Things that may be implemented later:
We use things like typeinfo
or dynamic_cast
around so we need RTTI enabled. However, when commenting out -fno-rtti
in CMakeLists.txt
, we get (as of dd59ee5 )
CMakeFiles/CCoscope.dir/codegencontext.cpp.o:(.rodata._ZTIN4llvm8FCmpInstE[_ZTIN4llvm8FCmpInstE]+0x10): undefined reference to `typeinfo for llvm::CmpInst'
CMakeFiles/CCoscope.dir/codegencontext.cpp.o:(.rodata._ZTIN4llvm8ICmpInstE[_ZTIN4llvm8ICmpInstE]+0x10): undefined reference to `typeinfo for llvm::CmpInst'
CMakeFiles/CCoscope.dir/codegencontext.cpp.o:(.rodata._ZTIN4llvm17GetElementPtrInstE[_ZTIN4llvm17GetElementPtrInstE]+0x10): undefined reference to `typeinfo for llvm::Instruction'
I think we need to either pass some additional flags to clang/llvm or rebuild it somehow so that it doesn't complain when using rtti. I don't believe people discard this feature when using llvm -- there must be a way to do that. Question is - how? :)
To implement this, the GlobalContext would need to store some information about the current loop we're codegenning in. It should be enough to store a pointer to the header block and to the merge block when entering a loop (while/for) codegen and restoring their previous value when returning from it. The break/continue statements would then simply insert a jump to such identified block.
I thought that having more folders and smaller files would make our project more friendly for maintenance, and a bit more readable -- how do you think?.
At e38753e resides my suggestion. However, CMake
is kind of magical for me and I'm having difficulty setting it up properly.
Each node in the AST should know what is the position in the source file that originated the expression associated with the AST. This is necessary for displaying line/column information alongside error messages.
We reached a point (at a4bd640 for instance) at which we should decide how our language will manage memory.
So should garbage collector:
Ownership
system as in Rust (https://doc.rust-lang.org/book/ownership.html)This comprises of having string literals, working =
and +
operators overloadings, being able to print strings. And maybe implicit conversions to String
.
This will probably entail adding Array
type support as well.
I think we should decide whether we want float
or double
datatype in our language -- there is a discrepancy between lexer.lex
file and the rest of the compiler.
Colouring the output messages should be simple, and will immediately make our compiler superior to gcc, as it is the only difference most programmers see between gcc and clang.
To make life easier when generating escaping from scopes (e.g. by means of break
keyword or ending blocks), it would be nice to have some Scope manager
.
Current ad-hoc solution is ugly and the flow should be more or less as follows: open scope -> generate code for block -> close scope
, where middle phase could jump to close scope
when bumped into break
, for instance.
It should also make implementation of invoking destructors (when added :) ) easier.
I refer to commit: 2dbb658
On examples/types.cco
, when printing f
variable, we get 0.0
instead of 3.14159
.
I'm not sure whether the root of the problem lays in CallExprAST::codegen
or DeclareCFunctions
or somewhere else...
I encountered a problem at 2377e90 . On examples/complex.cco
compilation crashes when codegenning newComplex
function (defined in CodegenContext::PrepareStdFunctionPrototypes
). As I understand the problem is that there hasn't happened an automatic load. And because I don't fully understand TypeMatcher
and conversions related stuff, I'd like to ask for help in nailing down the problem. I most probably screwed up something in ComplexValueAST::Resolve
or similar...
I think that we may codegen ReturnExprAST
incorrectly (or at least that we need to take a closer look at CFG contruction involving return statements).
As of a6c5139 , we produce the following LLVM IR:
define i32 @fib(i32 %n) {
entry:
%n_addr = alloca i32
store i32 %n, i32* %n_addr
%n1 = load i32, i32* %n_addr
%cmptmp = icmp uge i32 %n1, 0
%ifcond = icmp ne i1 %cmptmp, false
br i1 %ifcond, label %then, label %else
then: ; preds = %entry
%n2 = load i32, i32* %n_addr
%subtmp = sub i32 %n2, 2
%calltmp = call i32 @fib(i32 %subtmp)
%n3 = load i32, i32* %n_addr
%subtmp4 = sub i32 %n3, 1
%calltmp5 = call i32 @fib(i32 %subtmp4)
%addtmp = add i32 %calltmp, %calltmp5
ret i32 %addtmp
br label %ifcont
else: ; preds = %entry
ret i32 0
br label %ifcont
ifcont: ; preds = %else, %then
ret i32 0
}
and it seems like it falls into infinite loop. But I'm not sure.
And as a comparison, that's what clang
outputs on an equivalent fib.cpp
program with -O0
:
define i32 @_Z3fibi(i32 %n) #0 {
entry:
%retval = alloca i32, align 4
%n.addr = alloca i32, align 4
store i32 %n, i32* %n.addr, align 4
%0 = load i32, i32* %n.addr, align 4
%cmp = icmp sle i32 %0, 0
br i1 %cmp, label %if.then, label %if.else
if.then: ; preds = %entry
%1 = load i32, i32* %n.addr, align 4
%sub = sub nsw i32 %1, 2
%call = call i32 @_Z3fibi(i32 %sub)
%2 = load i32, i32* %n.addr, align 4
%sub1 = sub nsw i32 %2, 1
%call2 = call i32 @_Z3fibi(i32 %sub1)
%add = add nsw i32 %call, %call2
store i32 %add, i32* %retval, align 4
br label %return
if.else: ; preds = %entry
store i32 0, i32* %retval, align 4
br label %return
return: ; preds = %if.else, %if.then
%3 = load i32, i32* %retval, align 4
ret i32 %3
}
We can see that it's smart to have one ret
-- something we should try to achieve probably.
We should add the ability for the user to:
::
from C) -> but this may come as another task if turns out to be difficultAs of a34d6cd , I broke the gcd
example, because when we try to compile and link examples/gcd.cco
and examples/gcd_main.cco
, we have multiple definitions of newComplex
functions (which I consider a standard-library function for our compiler, but it's not as low level as things in libcco
to put it there). The problem is that I create this function in CodegenContext::PrepareStdFunctionPrototypes
and, well, when we compile more than one file, multiple definitions will certainly appear.
The question is: what would be the best place to put this creation of newComplex
? It seems to me that during compilation of "normal" files, we should emit just the prototype (in each of them), and then build a file with all the standard functions (currently only newComplex
but I think there will be more in the future?) codegenned there, and link all those files up.
Do you agree?
And a kind of related question -- why can't we emit IR code when we compile more than one file? I hope it's just an artificial obstacle, because if we decide to have this standard library, then we will always link at least two files and it shouldn't hinder us from peeking at IR.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.