Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
C
compiler-construction
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Requirements
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Locked files
Build
Pipelines
Jobs
Pipeline schedules
Test cases
Artifacts
Deploy
Releases
Container Registry
Model registry
Operate
Environments
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Code review analytics
Issue analytics
Insights
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
User expired
compiler-construction
Commits
8d81b229
Commit
8d81b229
authored
5 years ago
by
krf41037
Browse files
Options
Downloads
Patches
Plain Diff
Assembly generation parsing from tac
parent
84e5526a
No related branches found
Branches containing commit
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
src/assembly_gen.c
+295
-10
295 additions, 10 deletions
src/assembly_gen.c
with
295 additions
and
10 deletions
src/assembly_gen.c
+
295
−
10
View file @
8d81b229
#include
<stdio.h>
#include
<unistd.h>
#include
<sys/wait.h>
#include
"mcc/tac.h"
#include
"mcc/tac_print.h"
#include
"mcc/symbol_table.h"
char
*
mcc_asm_gen
(
FILE
*
out
,
struct
mcc_tac
*
tac
)
{
...
...
@@ -15,6 +13,7 @@ char *mcc_asm_gen(FILE *out, struct mcc_tac *tac)
{
struct
mcc_tac_entry
*
entry
=
tac
->
tac_entries
->
arr
[
i
];
char
*
arg1
=
entry
->
arg1
;
//TODO calculate offset and save temp offset
switch
(
entry
->
tac_op
)
{
...
...
@@ -24,36 +23,322 @@ char *mcc_asm_gen(FILE *out, struct mcc_tac *tac)
fprintf
(
out
,
"%s:
\n
"
,
arg1
);
fprintf
(
out
,
"
\t
pushl
\t
%%ebp
\n
"
);
fprintf
(
out
,
"
\t
movl
\t
%%esp, %%ebp
\n
"
);
break
;
break
;
case
MCC_TAC_CALL
:
fprintf
(
out
,
"
\t
call
\t
%s
\n
"
,
arg1
);
break
;
// unary
case
MCC_TAC_MINUS_INT_UN
:
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
offset
);
fprintf
(
out
,
"
\t
negl
\t
%%eax
\n
"
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
offset
);
break
;
case
MCC_TAC_MINUS_FLOAT_UN
:
break
;
case
MCC_TAC_NOT
:
fprintf
(
out
,
"
\t
negl
\t
%%eax
\n
"
);
break
;
// binary
case
MCC_TAC_PLUS_INT
:
fprintf
(
out
,
"
\t
addl
\t
%d(%%ebp), %%eax
\n
"
,
offset
);
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
addl
\t
%d(%%ebp), %%eax
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_MINUS_INT_BIN
:
fprintf
(
out
,
"
\t
subl
\t
%d(%%ebp), %%eax
\n
"
,
offset
);
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
subl
\t
%d(%%ebp), %%eax
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_MUL_INT
:
fprintf
(
out
,
"
\t
imull
\t
%d(%%ebp), %%eax
\n
"
,
offset
);
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
imull
\t
%d(%%ebp), %%eax
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_DIV_INT
:
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
cltd
\n
"
);
fprintf
(
out
,
"
\t
idivl
\t
%d(%%ebp)
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_PLUS_FLOAT
:
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
fadds
\t
%d(%%ebp)
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
fstps
\t
%d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_MINUS_FLOAT_BIN
:
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
fsubs
\t
%d(%%ebp)
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
fstps
\t
%d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_MUL_FLOAT
:
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
fmuls
\t
%d(%%ebp)
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
fstps
\t
%d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_DIV_FLOAT
:
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
fdivs
\t
%d(%%ebp)
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
fstps
\t
%d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_NOT_BOOL
:
fprintf
(
out
,
"
\t
xorl
\t
$1, %%edx
\n
"
);
break
;
case
MCC_TAC_AND
:
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
andl
\t
%d(%%ebp), %%edx
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_OR
:
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
orl
\t
%d(%%ebp), %%edx
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_EQ
:
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
cmpl
\t
%d(%%ebp), %%eax
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
sete
\t
%%al
\n
"
);
fprintf
(
out
,
"
\t
movzbl
\t
%%al, %%eax
\n
"
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_NEQ
:
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
cmpl
\t
%d(%%ebp), %%eax
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
setne
\t
%%al
\n
"
);
fprintf
(
out
,
"
\t
movzbl
\t
%%al, %%eax
\n
"
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_GT
:
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
cmpl
\t
%d(%%ebp), %%eax
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
setg
\t
%%al
\n
"
);
fprintf
(
out
,
"
\t
movzbl
\t
%%al, %%eax
\n
"
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_LT
:
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
cmpl
\t
%d(%%ebp), %%eax
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
setl
\t
%%al
\n
"
);
fprintf
(
out
,
"
\t
movzbl
\t
%%al, %%eax
\n
"
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_LTEQ
:
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
cmpl
\t
%d(%%ebp), %%eax
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
setle
\t
%%al
\n
"
);
fprintf
(
out
,
"
\t
movzbl
\t
%%al, %%eax
\n
"
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_GTEQ
:
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
cmpl
\t
%d(%%ebp), %%eax
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
setge
\t
%%al
\n
"
);
fprintf
(
out
,
"
\t
movzbl
\t
%%al, %%eax
\n
"
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_LT_FLOAT
:
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
fcomip
\t
%%st(1), %%st
\n
"
);
fprintf
(
out
,
"
\t
fstp
\t
%%st(0)
\n
"
);
fprintf
(
out
,
"
\t
setb
\t
%%al
\n
"
);
fprintf
(
out
,
"
\t
movzbl
\t
%%al, %%eax
\n
"
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_LTEQ_FLOAT
:
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
fcomip
\t
%%st(1), %%st
\n
"
);
fprintf
(
out
,
"
\t
fstp
\t
%%st(0)
\n
"
);
fprintf
(
out
,
"
\t
setbe
\t
%%al
\n
"
);
fprintf
(
out
,
"
\t
movzbl
\t
%%al, %%eax
\n
"
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_GT_FLOAT
:
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
fcomip
\t
%%st(1), %%st
\n
"
);
fprintf
(
out
,
"
\t
fstp
\t
%%st(0)
\n
"
);
fprintf
(
out
,
"
\t
seta
\t
%%al
\n
"
);
fprintf
(
out
,
"
\t
movzbl
\t
%%al, %%eax
\n
"
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_GTEQ_FLOAT
:
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
fcomip
\t
%%st(1), %%st
\n
"
);
fprintf
(
out
,
"
\t
fstp
\t
%%st(0)
\n
"
);
fprintf
(
out
,
"
\t
setae
\t
%%al
\n
"
);
fprintf
(
out
,
"
\t
movzbl
\t
%%al, %%eax
\n
"
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_EQ_FLOAT
:
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
fcomip
\t
%%st(1), %%st
\n
"
);
fprintf
(
out
,
"
\t
fstp
\t
%%st(0)
\n
"
);
fprintf
(
out
,
"
\t
sete
\t
%%al
\n
"
);
fprintf
(
out
,
"
\t
movzbl
\t
%%al, %%eax
\n
"
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_EQ_BOOL
:
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
cmpl
\t
%d(%%ebp), %%eax
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
sete
\t
%%al
\n
"
);
fprintf
(
out
,
"
\t
movzbl
\t
%%al, %%eax
\n
"
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_NEQ_FLOAT
:
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
fcomip
\t
%%st(1), %%st
\n
"
);
fprintf
(
out
,
"
\t
fstp
\t
%%st(0)
\n
"
);
fprintf
(
out
,
"
\t
setne
\t
%%al
\n
"
);
fprintf
(
out
,
"
\t
movzbl
\t
%%al, %%eax
\n
"
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
case
MCC_TAC_NEQ_BOOL
:
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
cmpl
\t
%d(%%ebp), %%eax
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
setne
\t
%%al
\n
"
);
fprintf
(
out
,
"
\t
movzbl
\t
%%al, %%eax
\n
"
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
break
;
// literal
case
MCC_TAC_BOOL_LITERAL
:
//TODO parse arg1 to 0 or 1??
int
bool_value
=
(
strcmp
(
arg1
,
"true"
)
==
0
)
?
1
:
0
;
fprintf
(
out
,
"
\t
movl
\t
$%s, %d(%%ebp)
\n
"
,
arg1
,
result
->
offset
);
break
;
case
MCC_TAC_INT_LITERAL
:
fprintf
(
out
,
"
\t
movl
\t
$%s, %d(%%ebp)
\n
"
,
arg1
,
result
->
offset
);
break
;
case
MCC_TAC_STRING_LITERAL
:
fprintf
(
out
,
"
\t
movl
\t
$.LCONST%d, %d(%%ebp)
\n
"
,
const_count
,
result
->
offset
);
break
;
case
MCC_TAC_FLOAT_LITERAL
:
//TODO create new constant
fprintf
(
out
,
"
\t
flds
\t
.LCONST%d
\n
"
,
const_count
);
fprintf
(
out
,
"
\t
fstps
\t
%d(%%ebp)
\n
"
,
result
->
offset
);
break
;
// copy
case
MCC_TAC_BOOL
:
break
;
case
MCC_TAC_INT
:
break
;
case
MCC_TAC_STRING
:
break
;
case
MCC_TAC_FLOAT
:
break
;
// push
case
MCC_TAC_PARAM_BOOL
:
case
MCC_TAC_PARAM_INT
:
case
MCC_TAC_PARAM_FLOAT
:
case
MCC_TAC_PARAM_STRING
:
fprintf
(
out
,
"
\t
pushl
\t
%d(%%ebp)
\n
"
,
arg1
->
offset
);
break
;
case
MCC_TAC_PARAM_BOOL_ARR
:
case
MCC_TAC_PARAM_INT_ARR
:
case
MCC_TAC_PARAM_FLOAT_ARR
:
case
MCC_TAC_PARAM_STRING_ARR
:
fprintf
(
out
,
"
\t
leal
\t
%d(%%ebp), %%eax
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
pushl
\t
%%eax
\n
"
);
break
;
// pop
case
MCC_TAC_PARAM_POP_BOOL
:
case
MCC_TAC_PARAM_POP_INT
:
case
MCC_TAC_PARAM_POP_FLOAT
:
case
MCC_TAC_PARAM_POP_STRING
:
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
param_offset
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, %d(%%ebp)
\n
"
,
result
->
offset
);
//increase param offset
break
;
case
MCC_TAC_PARAM_POP_BOOL_ARR
:
case
MCC_TAC_PARAM_POP_INT_ARR
:
case
MCC_TAC_PARAM_POP_FLOAT_ARR
:
case
MCC_TAC_PARAM_POP_STRING_ARR
:
//increase param offset
break
;
// load
case
MCC_TAC_LOAD_BOOL
:
case
MCC_TAC_LOAD_INT
:
case
MCC_TAC_LOAD_STRING
:
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%edx
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%ecx
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
movl
\t
0(%%ecx, %%edx, %d), %%eax
\n
"
,
4
);
break
;
case
MCC_TAC_LOAD_FLOAT
:
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%edx
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%ecx
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp, %%edx, %d)
\n
"
,
arg1
->
offset
,
4
);
break
;
// store
case
MCC_TAC_STORE_BOOL
:
case
MCC_TAC_STORE_INT
:
case
MCC_TAC_STORE_STRING
:
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%eax
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%edx
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%ecx
\n
"
,
offset
);
fprintf
(
out
,
"
\t
movl
\t
%%eax, 0(%%ecx, %%edx, %d)
\n
"
,
4
);
break
;
case
MCC_TAC_STORE_FLOAT
:
fprintf
(
out
,
"
\t
flds
\t
%d(%%ebp)
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%edx
\n
"
,
arg2
->
offset
);
fprintf
(
out
,
"
\t
movl
\t
%d(%%ebp), %%ecx
\n
"
,
offset
);
fprintf
(
out
,
"
\t
fstps
\t
0(%%ecx, %%edx, %d)
\n
"
,
4
);
break
;
// IR operations
case
MCC_TAC_JMP
:
fprintf
(
out
,
"
\t
jmp
\t
.%s
\n
"
,
result
+
sizeof
(
char
));
break
;
case
MCC_TAC_JMP_FALSE
:
fprintf
(
out
,
"
\t
cmpl
\t
$0, %d(%%ebp)
\n
"
,
arg1
->
offset
);
fprintf
(
out
,
"
\t
je
\t
.%s
\n
"
,
result
+
sizeof
(
char
));
break
;
case
MCC_TAC_RETURN
:
fprintf
(
out
,
"
\t
leave
\n
"
);
fprintf
(
out
,
"
\t
ret
\n
"
);
break
;
case
MCC_TAC_ARR_DECL
:
// set stack offset size of pointer * elements
break
;
case
MCC_TAC_LABEL
:
fprintf
(
out
,
".%s:
\n
"
,
result
+
sizeof
(
char
));
break
;
case
MCC_TAC_FUNCTION_END
:
fprintf
(
out
,
"
\t
leave
\n
"
);
fprintf
(
out
,
"
\t
ret
\n
"
);
break
;
default:
case
MCC_TAC_UNKNOWN
:
break
;
}
}
fprintf
(
out
,
"
\n
"
);
//TODO add constants
//fprintf(out, ".LCONST%d:\n", i);
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment