Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
H
hostCompiledSimulation
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Gaurav Kukreja
hostCompiledSimulation
Commits
37eafe49
Commit
37eafe49
authored
May 28, 2014
by
Gaurav Kukreja
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Reverted all python files in ir2c code, to solve issues
Signed-off-by:
Gaurav Kukreja
<
gaurav@gauravk.in
>
parent
f2aa5106
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
357 additions
and
388 deletions
+357
-388
AliasFile.py
ir2c/src/AliasFile.py
+26
-41
BlocksFile.py
ir2c/src/BlocksFile.py
+126
-128
CFile.py
ir2c/src/CFile.py
+87
-99
fn.py
ir2c/src/fn.py
+18
-22
ir2c.py
ir2c/src/ir2c.py
+3
-2
mem.py
ir2c/src/mem.py
+97
-96
No files found.
ir2c/src/AliasFile.py
View file @
37eafe49
#
import sys
import
sys
import
re
import
fn
import
logging
def
process
(
sourceFile
,
fnHash
):
"""
Reads the '.alias' file generated by the compiler frontend, to create a hash
table of the local and global variables accessed by a function in the source
code.
"""
# Open alias file
f
=
open
(
sourceFile
+
'.055t.alias'
);
line
=
f
.
readline
();
# Regular Expressions
re_fnStart
=
re
.
compile
(
';; Function'
);
re_word
=
re
.
compile
(
'
\
w+'
);
re_varStart
=
re
.
compile
(
'Referenced variables in '
);
re_varLine
=
re
.
compile
(
'Variable:'
);
re_getVar
=
re
.
compile
(
'Variable: |, |
\n
'
);
var_read
=
0
;
while
line
:
def
process
(
sourceFile
,
fnHash
):
f
=
open
(
sourceFile
+
'.055t.alias'
);
#'.t29.alias1');
line
=
f
.
readline
();
re_fnStart
=
re
.
compile
(
';; Function'
);
re_word
=
re
.
compile
(
'
\
w+'
);
re_varStart
=
re
.
compile
(
'Referenced variables in '
);
re_varLine
=
re
.
compile
(
'Variable:'
);
re_getVar
=
re
.
compile
(
'Variable: |, |
\n
'
);
var_read
=
0
while
line
:
if
re_fnStart
.
match
(
line
):
m
=
re_word
.
findall
(
line
);
fnName
=
m
[
1
];
fnHash
[
fnName
]
=
fn
.
Function
(
fnName
);
logging
.
debug
(
"Function Name: "
+
fnName
);
m
=
re_word
.
findall
(
line
);
fnName
=
m
[
1
];
fnHash
[
fnName
]
=
fn
.
Function
(
fnName
);
#print fnName; #############debugging
if
var_read
==
1
:
if
re_varLine
.
match
(
line
):
m
=
re_getVar
.
split
(
line
);
m
[
3
]
=
re
.
sub
(
r'\{.*\}'
,
''
,
m
[
3
]);
logging
.
debug
(
"
\t
Variable Name: "
+
m
[
1
]
+
", Variable Type: "
+
m
[
3
]);
fnHash
[
fnName
]
.
addVar
(
varName
=
m
[
1
]
.
strip
(),
varType
=
m
[
3
]
.
strip
());
elif
line
.
strip
():
var_read
=
0
;
if
re_varLine
.
match
(
line
):
m
=
re_getVar
.
split
(
line
);
m
[
3
]
=
re
.
sub
(
r'\{.*\}'
,
''
,
m
[
3
]);
fnHash
[
fnName
]
.
addVar
(
m
[
1
]
.
strip
(),
m
[
3
]
.
strip
());
elif
line
.
strip
():
var_read
=
0
;
if
re_varStart
.
match
(
line
):
var_read
=
1
;
line
=
f
.
readline
();
f
.
close
();
return
1
;
\ No newline at end of file
var_read
=
1
;
line
=
f
.
readline
();
f
.
close
();
return
1
;
ir2c/src/BlocksFile.py
View file @
37eafe49
import
re
#
import sys
#
import fn
import
sys
import
fn
import
mem
#20 Feb 2012 - modified var declaration end mark. There is no BLOCK 0.
#modified logic which sets memFlag. Earlier it wasn't getting set - Suhas
#31 Mar 2012 - added logic to preprocess block beginnings and convert
...
...
@@ -16,143 +15,142 @@ import mem
#8-10 Oct 2012 - removing [*_expr] sort of patterns.
def
process
(
sourceFile
,
fnHash
):
labelStart
=
"bb "
;
#
assuming that all labels names start with BB.
#
Change if label prefix changes - Suhas, 31 March 2012
f
=
open
(
sourceFile
+
".127t.blocks"
);
#".t99.blocks");
line
=
f
.
readline
();
printFlag
=
1
;
memFlag
=
0
;
currentFunction
=
"NONE"
;
var_decl
=
[];
#list to prevent redefinitions of variables
declFlag
=
0
;
re_comments
=
re
.
compile
(
'
\
s*#'
)
#Lines beginning with '#'
re_functionStart
=
re
.
compile
(
';; Function'
);
#Start of Function
#re_label1=re.compile('<[\s\w]+>:;'); #Labels
re_label1
=
re
.
compile
(
'<[
\
s
\
w]+>:'
);
#Labels
#re_label2=re.compile('<\w+>'); #Labels in goto statements
re_label2
=
re
.
compile
(
'<[
\
s
\
w]+>'
);
#Labels in goto statements
re_label3
=
re
.
compile
(
'<.+>
\
s*
\
((<
\
w+>)
\
)'
);
#re_var=re.compile('(\w+)\.(\d+)'); #For Compiler defined Variables with a '.' in them
re_var
=
re
.
compile
(
'([a-zA-Z]+
\
w*)
\
.(
\
d+)'
);
#For Compiler defined Variables with a '.' in them
#But doesn't change floating point constants.
re_numBytes
=
re
.
compile
(
'(
\
s+-*
\
d+)B'
);
#re_tailCall=re.compile('(\[tail call\];)');
re_tailCall
=
re
.
compile
(
'(
\
[tail call
\
])'
);
#removed ';' in regexp - Suhas, 12 Sep 2012
re_Block
=
re
.
compile
(
'
\
s*# BLOCK '
);
#first occurrence marks end of variable declarations
re_hexnum
=
re
.
compile
(
'
\
s+(0[0-9a-f]+)'
);
re_absExpr
=
re
.
compile
(
'ABS_EXPR <([
\
w
\
.-]+)>'
);
re_minExpr
=
re
.
compile
(
'MIN_EXPR <([
\
w
\
.-]+), ([
\
w
\
.-]+)>'
);
re_maxExpr
=
re
.
compile
(
'MAX_EXPR <([
\
w
\
.-]+), ([
\
w
\
.-]+)>'
);
re_rotrExpr
=
re
.
compile
(
'([
\
w
\
.-]+)
\
s*r>>
\
s*([0-9]+)'
);
re_rotlExpr
=
re
.
compile
(
'([
\
w
\
.-]+)
\
s*r<<
\
s*([0-9]+)'
);
re_exprToBeRemoved
=
re
.
compile
(
'
\
[(rshift|lshift|plus|minus|mult|bit_and|bit_ior|bit_xor|pointer_plus)_expr
\
]'
);
labelStart
=
"bb "
;
#
assuming that all labels names start with BB.
#
Change if label prefix changes - Suhas, 31 March 2012
f
=
open
(
sourceFile
+
".127t.blocks"
);
#".t99.blocks");
line
=
f
.
readline
();
printFlag
=
1
;
memFlag
=
0
;
currentFunction
=
"NONE"
;
var_decl
=
[];
#list to prevent redefinitions of variables
declFlag
=
0
;
re_comments
=
re
.
compile
(
'
\
s*#'
)
#Lines beginning with '#'
re_functionStart
=
re
.
compile
(
';; Function'
);
#Start of Function
#re_label1=re.compile('<[\s\w]+>:;'); #Labels
re_label1
=
re
.
compile
(
'<[
\
s
\
w]+>:'
);
#Labels
#re_label2=re.compile('<\w+>'); #Labels in goto statements
re_label2
=
re
.
compile
(
'<[
\
s
\
w]+>'
);
#Labels in goto statements
re_label3
=
re
.
compile
(
'<.+>
\
s*
\
((<
\
w+>)
\
)'
);
#re_var=re.compile('(\w+)\.(\d+)'); #For Compiler defined Variables with a '.' in them
re_var
=
re
.
compile
(
'([a-zA-Z]+
\
w*)
\
.(
\
d+)'
);
#For Compiler defined Variables with a '.' in them
#But doesn't change floating point constants.
re_numBytes
=
re
.
compile
(
'(
\
s+-*
\
d+)B'
);
#re_tailCall=re.compile('(\[tail call\];)');
re_tailCall
=
re
.
compile
(
'(
\
[tail call
\
])'
);
#removed ';' in regexp - Suhas, 12 Sep 2012
re_Block
=
re
.
compile
(
'
\
s*# BLOCK '
);
#first occurrence marks end of variable declarations
re_hexnum
=
re
.
compile
(
'
\
s+(0[0-9a-f]+)'
);
re_absExpr
=
re
.
compile
(
'ABS_EXPR <([
\
w
\
.-]+)>'
);
re_minExpr
=
re
.
compile
(
'MIN_EXPR <([
\
w
\
.-]+), ([
\
w
\
.-]+)>'
);
re_maxExpr
=
re
.
compile
(
'MAX_EXPR <([
\
w
\
.-]+), ([
\
w
\
.-]+)>'
);
re_rotrExpr
=
re
.
compile
(
'([
\
w
\
.-]+)
\
s*r>>
\
s*([0-9]+)'
);
re_rotlExpr
=
re
.
compile
(
'([
\
w
\
.-]+)
\
s*r<<
\
s*([0-9]+)'
);
re_exprToBeRemoved
=
re
.
compile
(
'
\
[(rshift|lshift|plus|minus|mult|bit_and|bit_ior|bit_xor|pointer_plus)_expr
\
]'
);
while
line
:
while
line
:
if
declFlag
==
1
:
if
re_Block
.
search
(
line
):
memFlag
=
1
;
declFlag
=
0
;
del
var_decl
[:];
#clear this list
if
re_Block
.
search
(
line
):
memFlag
=
1
;
declFlag
=
0
;
del
var_decl
[:];
#clear this list
if
declFlag
==
1
:
if
not
(
line
in
var_decl
)
:
#prevent redefinitions of variables
var_decl
.
append
(
line
);
var_decl
.
append
(
line
);
# replace type of pointer variables
decl
=
re
.
split
(
r'([\w_.]+;)'
,
line
)
if
len
(
decl
)
==
3
and
decl
[
0
]
.
find
(
"unsigned"
)
>=
0
:
var
=
decl
[
1
][:
-
1
]
if
var
not
in
fnHash
[
currentFunction
]
.
getVars
()
.
keys
()
and
\
(
var
.
startswith
(
"ivtmp."
)
or
var
.
startswith
(
"D."
)
or
\
var
.
startswith
(
"W."
)):
line
=
re
.
split
(
r'\S+'
,
decl
[
0
])[
0
]
+
"uintptr_t "
+
decl
[
1
]
+
decl
[
2
]
var
=
decl
[
1
][:
-
1
]
if
var
not
in
fnHash
[
currentFunction
]
.
getVars
()
.
keys
()
and
\
(
var
.
startswith
(
"ivtmp."
)
or
var
.
startswith
(
"D."
)
or
\
var
.
startswith
(
"W."
)):
line
=
re
.
split
(
r'\S+'
,
decl
[
0
])[
0
]
+
"uintptr_t "
+
decl
[
1
]
+
decl
[
2
]
if
len
(
decl
)
==
3
and
decl
[
0
]
.
find
(
'*'
)
>=
0
:
line
=
re
.
sub
(
r'\[[0-9]+\]'
,
''
,
decl
[
0
])
+
decl
[
1
]
+
decl
[
2
]
line
=
re
.
sub
(
r'\[[0-9]+\]'
,
''
,
decl
[
0
])
+
decl
[
1
]
+
decl
[
2
]
else
:
printFlag
=
0
;
if
(
re_comments
.
match
(
line
)):
m
=
re_Block
.
search
(
line
)
if
m
!=
None
:
temp
=
line
[
m
.
end
():]
n
=
re
.
search
(
'[0-9]+'
,
temp
)
#search and extract basic block number
if
n
!=
None
:
bbNum
=
temp
[:
n
.
end
()]
line
=
'<'
+
labelStart
+
bbNum
+
'>'
+
':'
#print "Debug!! " + line
else
:
line
=
"//"
+
line
;
printFlag
=
0
;
if
(
re_comments
.
match
(
line
)):
m
=
re_Block
.
search
(
line
)
if
m
!=
None
:
temp
=
line
[
m
.
end
():]
n
=
re
.
search
(
'[0-9]+'
,
temp
)
#search and extract basic block number
if
n
!=
None
:
bbNum
=
temp
[:
n
.
end
()]
line
=
'<'
+
labelStart
+
bbNum
+
'>'
+
':'
#print "Debug!! " + line
else
:
line
=
"//"
+
line
;
if
re
.
match
(
'Invalid sum of incoming frequencies'
,
line
):
line
=
"//"
+
line
;
line
=
"//"
+
line
;
elif
re
.
match
(
'Invalid sum of outgoing probabilities'
,
line
):
line
=
"//"
+
line
;
line
=
"//"
+
line
;
elif
(
re_functionStart
.
match
(
line
)):
printFlag
=
0
;
m
=
re
.
search
(
'
\
w+
\
s*
\
('
,
line
);
m
=
re
.
search
(
'
\
w+'
,
m
.
group
());
currentFunction
=
m
.
group
();
f
.
readline
();
f
.
readline
();
printFlag
=
0
;
print
fnHash
[
currentFunction
]
.
getDesc
(),;
declFlag
=
1
;
memFlag
=
0
;
printFlag
=
0
;
m
=
re
.
search
(
'
\
w+
\
s*
\
('
,
line
);
m
=
re
.
search
(
'
\
w+'
,
m
.
group
());
currentFunction
=
m
.
group
();
f
.
readline
();
f
.
readline
();
printFlag
=
0
;
print
fnHash
[
currentFunction
]
.
getDesc
(),;
declFlag
=
1
;
memFlag
=
0
;
elif
currentFunction
!=
"NONE"
:
line
=
re_numBytes
.
sub
(
'
\
g<1>'
,
line
);
m
=
re_absExpr
.
search
(
line
)
if
m
:
if
m
.
group
(
1
)
in
fnHash
[
currentFunction
]
.
getVars
()
.
keys
():
v_t
=
fnHash
[
currentFunction
]
.
getVars
()[
m
.
group
(
1
)]
if
v_t
.
find
(
"long double"
)
>=
0
:
line
=
re_absExpr
.
sub
(
'fabsl(
\
g<1>)'
,
line
)
elif
v_t
.
find
(
"double"
)
>=
0
:
line
=
re_absExpr
.
sub
(
'fabs(
\
g<1>)'
,
line
)
elif
v_t
.
find
(
"float"
)
>=
0
:
line
=
re_absExpr
.
sub
(
'fabsf(
\
g<1>)'
,
line
)
elif
v_t
.
find
(
"long long"
)
>=
0
:
line
=
re_absExpr
.
sub
(
'llabs(
\
g<1>)'
,
line
)
elif
v_t
.
find
(
"long"
)
>=
0
:
line
=
re_absExpr
.
sub
(
'labs(
\
g<1>)'
,
line
)
elif
v_t
.
find
(
"int"
)
>=
0
or
v_t
.
find
(
"short"
)
>=
0
:
line
=
re_absExpr
.
sub
(
'abs(
\
g<1>)'
,
line
)
line
=
re_absExpr
.
sub
(
'(
\
g<1>>0)?
\
g<1>:-
\
g<1>'
,
line
);
line
=
re_minExpr
.
sub
(
'(
\
g<1><
\
g<2>)?
\
g<1>:
\
g<2>'
,
line
);
line
=
re_maxExpr
.
sub
(
'(
\
g<1>>
\
g<2>)?
\
g<1>:
\
g<2>'
,
line
);
line
=
re_rotrExpr
.
sub
(
'(
\
g<1><<
\
g<2>)|(
\
g<1>>>(sizeof(
\
g<1>)*CHAR_BIT-
\
g<2>))'
,
line
);
line
=
re_rotlExpr
.
sub
(
'(
\
g<1>>>
\
g<2>)|(
\
g<1><<(sizeof(
\
g<1>)*CHAR_BIT-
\
g<2>))'
,
line
);
line
=
re_exprToBeRemoved
.
sub
(
''
,
line
);
#remove other types of [*_expr] tags
if
memFlag
:
line
=
mem
.
memAccess
(
line
,
fnHash
[
currentFunction
]);
line
=
re_label3
.
sub
(
'
\
g<1>'
,
line
);
#line1= re_label3.sub('\g<1>',line); #uncomment above line and comment these 4 not debugging
#if line1 != line:
# print "Label type 3 matched"
# print line; #DEBUG print only
#line = line1
if
re_label1
.
search
(
line
):
m
=
re
.
search
(
'[
\
s
\
w]+'
,
line
);
line
=
(
currentFunction
+
m
.
group
()
+
":
\n
"
)
line
=
re
.
sub
(
'
\
s+'
,
'_'
,
line
,
1
)
#print "Label type 1 matched"
line
=
re_numBytes
.
sub
(
'
\
g<1>'
,
line
);
m
=
re_absExpr
.
search
(
line
)
if
m
:
if
m
.
group
(
1
)
in
fnHash
[
currentFunction
]
.
getVars
()
.
keys
():
v_t
=
fnHash
[
currentFunction
]
.
getVars
()[
m
.
group
(
1
)]
if
v_t
.
find
(
"long double"
)
>=
0
:
line
=
re_absExpr
.
sub
(
'fabsl(
\
g<1>)'
,
line
)
elif
v_t
.
find
(
"double"
)
>=
0
:
line
=
re_absExpr
.
sub
(
'fabs(
\
g<1>)'
,
line
)
elif
v_t
.
find
(
"float"
)
>=
0
:
line
=
re_absExpr
.
sub
(
'fabsf(
\
g<1>)'
,
line
)
elif
v_t
.
find
(
"long long"
)
>=
0
:
line
=
re_absExpr
.
sub
(
'llabs(
\
g<1>)'
,
line
)
elif
v_t
.
find
(
"long"
)
>=
0
:
line
=
re_absExpr
.
sub
(
'labs(
\
g<1>)'
,
line
)
elif
v_t
.
find
(
"int"
)
>=
0
or
v_t
.
find
(
"short"
)
>=
0
:
line
=
re_absExpr
.
sub
(
'abs(
\
g<1>)'
,
line
)
line
=
re_absExpr
.
sub
(
'(
\
g<1>>0)?
\
g<1>:-
\
g<1>'
,
line
);
line
=
re_minExpr
.
sub
(
'(
\
g<1><
\
g<2>)?
\
g<1>:
\
g<2>'
,
line
);
line
=
re_maxExpr
.
sub
(
'(
\
g<1>>
\
g<2>)?
\
g<1>:
\
g<2>'
,
line
);
line
=
re_rotrExpr
.
sub
(
'(
\
g<1><<
\
g<2>)|(
\
g<1>>>(sizeof(
\
g<1>)*CHAR_BIT-
\
g<2>))'
,
line
);
line
=
re_rotlExpr
.
sub
(
'(
\
g<1>>>
\
g<2>)|(
\
g<1><<(sizeof(
\
g<1>)*CHAR_BIT-
\
g<2>))'
,
line
);
line
=
re_exprToBeRemoved
.
sub
(
''
,
line
);
#remove other types of [*_expr] tags
if
memFlag
:
line
=
mem
.
memAccess
(
line
,
fnHash
[
currentFunction
]);
line
=
re_label3
.
sub
(
'
\
g<1>'
,
line
);
#line1= re_label3.sub('\g<1>',line); #uncomment above line and comment these 4 not debugging
#if line1 != line:
# print "Label type 3 matched"
# print line; #DEBUG print only
#line = line1
if
re_label1
.
search
(
line
):
m
=
re
.
search
(
'[
\
s
\
w]+'
,
line
);
line
=
(
currentFunction
+
m
.
group
()
+
":
\n
"
)
line
=
re
.
sub
(
'
\
s+'
,
'_'
,
line
,
1
)
#print "Label type 1 matched"
#print line; #DEBUG print only
else
:
m
=
re_label2
.
findall
(
line
)
for
i
in
m
:
m2
=
re
.
search
(
'[
\
s
\
w]+'
,
i
)
subString
=
currentFunction
+
m2
.
group
()
subString
=
re
.
sub
(
'
\
s+'
,
'_'
,
subString
,
1
)
#line = re_label2.sub(currentFunction+m2.group(),line,1)
line
=
re_label2
.
sub
(
subString
,
line
,
1
)
#line1 = re_label2.sub(currentFunction+m2.group(),line,1)
#if line1 == line: #if substitution did not take place
# line = line1 #retain line
#else: # else replace space by underscore
# line = re.sub( '\s+', '_', line1,1)
#print "Label type 2 matched"
#print line; #DEBUG print only
else
:
m
=
re_label2
.
findall
(
line
)
for
i
in
m
:
m2
=
re
.
search
(
'[
\
s
\
w]+'
,
i
)
subString
=
currentFunction
+
m2
.
group
()
subString
=
re
.
sub
(
'
\
s+'
,
'_'
,
subString
,
1
)
#line = re_label2.sub(currentFunction+m2.group(),line,1)
line
=
re_label2
.
sub
(
subString
,
line
,
1
)
#line1 = re_label2.sub(currentFunction+m2.group(),line,1)
#if line1 == line: #if substitution did not take place
# line = line1 #retain line
#else: # else replace space by underscore
# line = re.sub( '\s+', '_', line1,1)
#print "Label type 2 matched"
#print line; #DEBUG print only
line
=
re_var
.
sub
(
'
\
g<1>_
\
g<2>'
,
line
);
line
=
re_tailCall
.
sub
(
'//
\
g<1>'
,
line
);
line
=
re_hexnum
.
sub
(
' 0x
\
g<1>'
,
line
);
if
printFlag
==
1
:
print
line
,
line
=
re_var
.
sub
(
'
\
g<1>_
\
g<2>'
,
line
);
line
=
re_tailCall
.
sub
(
'//
\
g<1>'
,
line
);
line
=
re_hexnum
.
sub
(
' 0x
\
g<1>'
,
line
);
if
printFlag
==
1
:
print
line
,
printFlag
=
1
;
line
=
f
.
readline
()
return
1
;
line
=
f
.
readline
()
return
1
;
ir2c/src/CFile.py
View file @
37eafe49
import
sys
import
re
import
fn
import
keywords
import
logging
#2 April 2012 - Original function line pattern doesn't work when fn args are
# in different lines. Changing logic to check for that
...
...
@@ -9,132 +10,119 @@ import logging
# Not completely verified though
def
readGlobalData
(
sourceFile
,
fnHash
):
"""
Reads Global Data like comments, include macros and global declarations,
which are not included in the Blocks File.
"""
f
=
open
(
sourceFile
);
line
=
f
.
readline
();
stopPrint
=
0
;
comment
=
0
;
fnAltArgStart
=
0
;
fnName
=
""
re_ppDirective
=
re
.
compile
(
'
\
s*#'
)
re_commentStart
=
re
.
compile
(
'/
\
*'
);
re_commentEnd
=
re
.
compile
(
'
\
*/'
);
re_comment
=
re
.
compile
(
'
\
s*//'
);
re_functionLine
=
re
.
compile
(
'[
\
w
\
s]*
\
w+
\
s+
\
w+
\
s*
\
(.*
\
)
\
s*($|
\
{)'
);
# pattern to match function with pointer return type.
re_functionLine2
=
re
.
compile
(
'[
\
w
\
s]*
\
w+
\
s*
\
*
\
s*
\
w+
\
s*
\
(.*
\
)
\
s*($|
\
{)'
);
re_fnLineAltStart
=
re
.
compile
(
'[
\
w
\
s]*
\
w+
\
s+
\
w+
\
s*
\
([^)]*$'
);
# multiline fn arg list
re_fnLineAltEnd
=
re
.
compile
(
'.*
\
)
\
s*($|
\
{)'
);
re_functionDesc
=
re
.
compile
(
'.+
\
)'
)
# remove comments or '{' from function line
re_functionName
=
re
.
compile
(
'
\
w+
\
s*
\
('
)
lineNum
=
0
commentLineNum
=
0
m
=
None
fnDesc_b4_Comment
=
0
while
line
:
f
=
open
(
sourceFile
);
line
=
f
.
readline
();
stopPrint
=
0
;
comment
=
0
;
fnAltArgStart
=
0
;
fnName
=
""
re_ppDirective
=
re
.
compile
(
'
\
s*#'
)
re_commentStart
=
re
.
compile
(
'/
\
*'
);
re_commentEnd
=
re
.
compile
(
'
\
*/'
);
re_comment
=
re
.
compile
(
'
\
s*//'
);
re_functionLine
=
re
.
compile
(
'[
\
w
\
s]*
\
w+
\
s+
\
w+
\
s*
\
(.*
\
)
\
s*($|
\
{)'
);
#pattern to match function with pointer return type.
re_functionLine2
=
re
.
compile
(
'[
\
w
\
s]*
\
w+
\
s*
\
*
\
s*
\
w+
\
s*
\
(.*
\
)
\
s*($|
\
{)'
);
re_fnLineAltStart
=
re
.
compile
(
'[
\
w
\
s]*
\
w+
\
s+
\
w+
\
s*
\
([^)]*$'
);
#multiline fn arg list
re_fnLineAltEnd
=
re
.
compile
(
'.*
\
)
\
s*($|
\
{)'
);
re_functionDesc
=
re
.
compile
(
'.+
\
)'
)
#remove comments or '{' from function line
re_functionName
=
re
.
compile
(
'
\
w+
\
s*
\
('
)
lineNum
=
0
commentLineNum
=
0
m
=
None
fnDesc_b4_Comment
=
0
while
line
:
lineNum
=
lineNum
+
1
# if not in the middle of a multiline comment and not in the middle of
# multiline fn arg list
#if not in the middle of a multiline comment and not in the middle of
#multiline fn arg list
if
(
comment
==
0
)
and
(
not
fnAltArgStart
):
m
=
re_functionLine
.
search
(
line
);
m
=
re_functionLine
.
search
(
line
);
if
m
==
None
:
m
=
re_fnLineAltStart
.
search
(
line
)
if
m
==
None
:
m
=
re_fnLineAltStart
.
search
(
line
)
if
m
==
None
:
m
=
re_functionLine2
.
search
(
line
);
m
=
re_functionLine2
.
search
(
line
);
cM
=
re_commentStart
.
search
(
line
)
if
cM
!=
None
:
comment
=
1
;
commentLineNum
=
lineNum
if
m
!=
None
:
if
(
m
.
start
()
<
cM
.
start
()
):
fnDesc_b4_Comment
=
1
comment
=
1
;
commentLineNum
=
lineNum
if
m
!=
None
:
if
(
m
.
start
()
<
cM
.
start
()
):
fnDesc_b4_Comment
=
1
cS
=
re_comment
.
match
(
line
)
if
cS
!=
None
:
commentLineNum
=
lineNum
if
m
!=
None
:
if
(
m
.
start
()
<
cS
.
start
()
):
fnDesc_b4_Comment
=
1
commentLineNum
=
lineNum
if
m
!=
None
:
if
(
m
.
start
()
<
cS
.
start
()
):
fnDesc_b4_Comment
=
1
if
(
((
cS
==
None
)
and
(
comment
==
0
))
or
\
((
commentLineNum
==
lineNum
)
and
(
fnDesc_b4_Comment
))
)
\
and
(
not
re_ppDirective
.
match
(
line
)):
# in function params, if only "short" or "long" is mentioned,
# replaces it with "short int" or "long int" respectively.
line
=
re
.
sub
(
r'(^|\s)long(\s+)(?!(int|double))'
,
'
\
g<1>long int
\
g<2>'
,
line
)
line
=
re
.
sub
(
r'(^|\s)short(\s+)(?!(int|double))'
,
'
\
g<1>short int
\
g<2>'
,
line
)
m
=
re_functionLine
.
search
(
line
);
if
m
==
None
:
m
=
re_functionLine2
.
search
(
line
);
if
m
and
(
not
fnAltArgStart
):
((
commentLineNum
==
lineNum
)
and
(
fnDesc_b4_Comment
))
)
\
and
(
not
re_ppDirective
.
match
(
line
)):
line
=
re
.
sub
(
r'(^|\s)long(\s+)(?!(int|double))'
,
'
\
g<1>long int
\
g<2>'
,
line
)
line
=
re
.
sub
(
r'(^|\s)short(\s+)(?!(int|double))'
,
'
\
g<1>short int
\
g<2>'
,
line
)
m
=
re_functionLine
.
search
(
line
);
if
m
==
None
:
m
=
re_functionLine2
.
search
(
line
);
if
m
and
(
not
fnAltArgStart
):
m
=
re_functionDesc
.
match
(
m
.
group
());
fnDesc
=
m
.
group
();
m
=
re_functionName
.
search
(
fnDesc
);
fnDesc
=
m
.
group
();
m
=
re_functionName
.
search
(
fnDesc
);
m
=
re
.
match
(
'
\
w+'
,
m
.
group
());
fnName
=
m
.
group
();
logging
.
debug
(
"In main Fnmatch pattern, Fn = "
+
fnName
)
if
fnName
in
fnHash
:
# This could be a function within ifdef, which is finally not defined
if
not
(
fnName
in
keywords
.
keywords
):
fnHash
[
fnName
]
.
setDesc
(
fnDesc
);
logging
.
debug
(
"In main Fnmatch pattern, Fn desc = "
+
fnDesc
)
stopPrint
=
1
;
fnName
=
m
.
group
();
#print"Debug. In main Fnmatch pattern\nFn = " + fnName
if
fnName
in
fnHash
:
#This could be a function within ifdef,
#which is finally not defined
if
not
(
fnName
in
keywords
.
keywords
):
fnHash
[
fnName
]
.
setDesc
(
fnDesc
);
#print"Debug. In main Fnmatch pattern\nFn desc = " + fnDesc
stopPrint
=
1
;
# This is part of multiline fn arg list pattern search.
# pushing it above the search for start of multiline arg list
# so that it doesn't repeat the recording of the first line of the list
if
fnAltArgStart
:
# still going through function arg list
if
fnName
in
fnHash
:
# This could be a function within ifdef,
# which is finally not defined, absent in Alias file
curFnDesc
=
fnHash
[
fnName
]
.
getDesc
();
newDesc
=
curFnDesc
+
line
;
fnHash
[
fnName
]
.
setDesc
(
newDesc
);
m
=
re_fnLineAltStart
.
search
(
line
);
# main pattern doesn't match multiline function arg list
if
m
and
(
not
fnAltArgStart
):
#This is an alternate search pattern
fnAltArgStart
=
1
;
#function arg list starts
#This is part of multiline fn arg list pattern search.
#pushing it above the search for start of multiline arg list
#so that it doesn't repeat the recording of the first line of the list
if
fnAltArgStart
:
#still going through function arg list
if
fnName
in
fnHash
:
#This could be a function within ifdef,
#which is finally not defined, absent in Alias file
curFnDesc
=
fnHash
[
fnName
]
.
getDesc
();
newDesc
=
curFnDesc
+
line
;
fnHash
[
fnName
]
.
setDesc
(
newDesc
);
m
=
re_fnLineAltStart
.
search
(
line
);
#main pattern doesn't match multiline function arg list
if
m
and
(
not
fnAltArgStart
):
#This is an alternate search pattern
fnAltArgStart
=
1
;
#function arg list starts
m
=
re_functionName
.
search
(
m
.
group
());
m
=
re
.
match
(
'
\
w+'
,
m
.
group
());
fnName
=
m
.
group
();
#print"Debug. In Alt. Fnstartmatch pattern \nFn = " + fnName
if
fnName
in
fnHash
:
#This could be a function within ifdef,
#which is finally not defined
if
not
(
fnName
in
keywords
.
keywords
):
fnHash
[
fnName
]
.
setDesc
(
line
);
#add description line by line
#print"Debug. In Alt. Fnstartmatch pattern\nFn desc = " + fnHash[fnName].getDesc()
stopPrint
=
1
;
if
fnName
in
fnHash
:
#This could be a function within ifdef,
#which is finally not defined
if
not
(
fnName
in
keywords
.
keywords
):
fnHash
[
fnName
]
.
setDesc
(
line
);
#add description line by line
#print"Debug. In Alt. Fnstartmatch pattern\nFn desc = " + fnHash[fnName].getDesc()
stopPrint
=
1
;
m
=
re_fnLineAltEnd
.
search
(
line
);
#search for end of fn arg list
if
m
and
fnAltArgStart
:
m
=
re_fnLineAltEnd
.
search
(
line
);
#search for end of fn arg list
if
m
and
fnAltArgStart
:
fnAltArgStart
=
0
;
#function arg list ends
#if fnName in fnHash: #Debug
# print"Debug. Fn desc = " + fnHash[fnName].getDesc()
if
re_commentEnd
.
search
(
line
):
comment
=
0
;
comment
=
0
;
fnDesc_b4_Comment
=
0
if
stopPrint
==
0
:
#print "Debug. Printing lines from C file"
print
line
,;
line
=
f
.
readline
();
f
.
close
();
return
1
;
#print "Debug. Printing lines from C file"
print
line
,;
line
=
f
.
readline
();
f
.
close
();
return
1
;
ir2c/src/fn.py
View file @
37eafe49
import
re
class
Function
:
def
__init__
(
self
,
name
):
self
.
name
=
name
;
self
.
varHash
=
{};
def
__init__
(
self
,
name
):
self
.
name
=
name
;
self
.
varHash
=
{};
self
.
ptrList
=
[];
def
setDesc
(
self
,
desc
):
self
.
desc
=
desc
;
def
getDesc
(
self
):
#print self.name;
return
self
.
desc
;
def
getName
(
self
):
return
self
.
name
;
def
addVar
(
self
,
varName
,
varType
):
self
.
varHash
[
varName
]
=
varType
;
def
setDesc
(
self
,
desc
):
self
.
desc
=
desc
;
def
getDesc
(
self
):
#print self.name;
return
self
.
desc
;
def
getName
(
self
):
return
self
.
name
;
def
addVar
(
self
,
varName
,
varType
):
self
.
varHash
[
varName
]
=
varType
;
if
re
.
search
(
'[
\
*
\
[]'
,
varType
):
self
.
ptrList
.
append
(
varName
);
self
.
ptrList
.
append
(
varName
);
def
getVars
(
self
):
return
self
.
varHash
;
def
getPtrs
(
self
):
return
self
.
ptrList
;
def
getVars
(
self
):
return
self
.
varHash
;
def
getPtrs
(
self
):
return
self
.
ptrList
;
ir2c/src/ir2c.py
View file @
37eafe49
import
sys
import
re
import
CFile
import
BlocksFile
import
fn
import
AliasFile
sourceFile
=
sys
.
argv
[
1
];
...
...
@@ -17,8 +19,7 @@ print ""
print
" ***********************************************************/"
print
"#include <limits.h>"
print
"#include <stdint.h>"
print
"#include
<ir2c.h>
"
print
"#include
\"
ir2c.h
\"
"
print
""
CFile
.
readGlobalData
(
sourceFile
,
fnHash
);
BlocksFile
.
process
(
sourceFile
,
fnHash
);
ir2c/src/mem.py
View file @
37eafe49
import
re
import
fn
import
keywords
TARGET_BITWIDTH
=
32
...
...
@@ -17,22 +18,22 @@ def memAccess(line,func):
if
i
%
2
==
1
:
line
=
line
+
a
[
i
];
else
:
if
re_indirection
.
search
(
a
[
i
]):
# Find pointer type for indirection operations
a
[
i
]
=
resolveIndirection
(
a
[
i
],
func
.
getVars
());
m
=
re_mem
.
split
(
a
[
i
]);
a
[
i
]
=
""
for
j
in
range
(
len
(
m
)):
if
j
%
2
==
1
:
a
[
i
]
=
a
[
i
]
+
resolveMEM
(
m
[
j
],
func
,
a
[
0
]
.
strip
());
else
:
# Remove unnecessary pointer type casts
m
[
j
]
=
removePtrCast
(
m
[
j
]);
# Cast Ptrs where pointer arithmetic is taking place
m
[
j
]
=
resolvePtrMath
(
m
[
j
],
func
.
getPtrs
());
a
[
i
]
=
a
[
i
]
+
m
[
j
];
# special handling for some bugs in IR operations
line
=
line
+
re_invert
.
sub
(
'
\
g<1> = ~
\
g<1>'
,
a
[
i
])
if
re_indirection
.
search
(
a
[
i
]):
# Find pointer type for indirection operations
a
[
i
]
=
resolveIndirection
(
a
[
i
],
func
.
getVars
());
m
=
re_mem
.
split
(
a
[
i
]);
a
[
i
]
=
""
for
j
in
range
(
len
(
m
)):
if
j
%
2
==
1
:
a
[
i
]
=
a
[
i
]
+
resolveMEM
(
m
[
j
],
func
,
a
[
0
]
.
strip
());
else
:
# Remove unnecessary pointer type casts
m
[
j
]
=
removePtrCast
(
m
[
j
]);
# Cast Ptrs where pointer arithmetic is taking place
m
[
j
]
=
resolvePtrMath
(
m
[
j
],
func
.
getPtrs
());
a
[
i
]
=
a
[
i
]
+
m
[
j
];
# special handling for some bugs in IR operations
line
=
line
+
re_invert
.
sub
(
'
\
g<1> = ~
\
g<1>'
,
a
[
i
])
return
line
;
...
...
@@ -42,7 +43,7 @@ def resolveMEM(mem,func,lhs):
varHash
=
func
.
getVars
()
re_memsplit
=
re
.
compile
(
'
\
w+: [^,|^
\
]]+'
);
#('\w+: [\w|\.]+')
re_tokensplit
=
re
.
compile
(
': '
);
#
re_plus=re.compile('\+\s*(\([\w\s]+\))?');
re_plus
=
re
.
compile
(
'
\
+
\
s*(
\
([
\
w
\
s]+
\
))?'
);
re_cast
=
re
.
compile
(
'
\
s*
\
([
\
w
\
s]+
\
)'
)
mem_components
=
{};
# resolve type
...
...
@@ -52,7 +53,7 @@ def resolveMEM(mem,func,lhs):
t
=
re
.
match
(
'[
\
w]+'
,
mtype
);
mtype
=
t
.
group
();
if
mtype
.
startswith
(
'struct'
)
and
\
(
m
.
group
()
.
find
(
'.'
)
>=
0
or
m
.
group
()
.
find
(
'->'
)
>=
0
):
(
m
.
group
()
.
find
(
'.'
)
>=
0
or
m
.
group
()
.
find
(
'->'
)
>=
0
):
# struct member access, try to get type from LHS
m1
=
re
.
match
(
r'\*\(([\w\s]+)\*\s*\)'
,
lhs
)
m2
=
re
.
match
(
r'\(([\w\s]+)\)'
,
lhs
)
...
...
@@ -70,31 +71,31 @@ def resolveMEM(mem,func,lhs):
mem
=
""
;
plus_sign
=
0
;
for
token
in
m
:
t
=
re_tokensplit
.
split
(
token
)
mem_components
[
t
[
0
]]
=
t
[
1
];
t
=
re_tokensplit
.
split
(
token
)
mem_components
[
t
[
0
]]
=
t
[
1
];
if
"symbol"
in
mem_components
:
mem
=
mem
+
UINTPTR_CAST
+
re_cast
.
sub
(
''
,
mem_components
[
"symbol"
])
plus_sign
=
1
;
mem
=
mem
+
UINTPTR_CAST
+
re_cast
.
sub
(
''
,
mem_components
[
"symbol"
])
plus_sign
=
1
;
if
"base"
in
mem_components
:
if
plus_sign
:
mem
=
mem
+
" + "
mem
=
mem
+
resolvePtrMath
(
mem_components
[
"base"
],
func
.
getPtrs
(),
1
)
plus_sign
=
1
;
if
plus_sign
:
mem
=
mem
+
" + "
mem
=
mem
+
resolvePtrMath
(
mem_components
[
"base"
],
func
.
getPtrs
(),
1
)
plus_sign
=
1
;
if
"index"
in
mem_components
:
if
plus_sign
:
mem
=
mem
+
" + "
;
mem
=
mem
+
UINTPTR_CAST
+
re_cast
.
sub
(
''
,
mem_components
[
"index"
])
if
plus_sign
:
mem
=
mem
+
" + "
;
mem
=
mem
+
UINTPTR_CAST
+
re_cast
.
sub
(
''
,
mem_components
[
"index"
])
if
"step"
in
mem_components
:
step
=
re
.
match
(
'
\
d+'
,
mem_components
[
"step"
])
mem
=
mem
+
" * "
+
step
.
group
();
plus_sign
=
1
;
step
=
re
.
match
(
'
\
d+'
,
mem_components
[
"step"
])
mem
=
mem
+
" * "
+
step
.
group
();
plus_sign
=
1
;
if
"offset"
in
mem_components
:
mem
=
mem
+
" + "
mem_components
[
"offset"
]
=
mem_components
[
"offset"
]
.
strip
()
if
mem_components
[
"offset"
]
.
isdigit
():
if
int
(
mem_components
[
"offset"
])
>=
2
**
(
TARGET_BITWIDTH
-
1
):
mem
=
mem
+
"(int)"
mem
=
mem
+
mem_components
[
"offset"
];
mem
=
mem
+
" + "
mem_components
[
"offset"
]
=
mem_components
[
"offset"
]
.
strip
()
if
mem_components
[
"offset"
]
.
isdigit
():
if
int
(
mem_components
[
"offset"
])
>=
2
**
(
TARGET_BITWIDTH
-
1
):
mem
=
mem
+
"(int)"
mem
=
mem
+
mem_components
[
"offset"
];
mem
=
"*("
+
mtype
+
"*)("
+
mem
+
")"
return
mem
;
...
...
@@ -109,8 +110,8 @@ def resolveIndirection(istr, varHash):
re_dataType
=
re
.
compile
(
'
\
*|
\
['
);
#('\s*[^\w]'); # Regex to remove * , [nn] etc from var Type -- e.g int[64] becomes int
m
=
re_1
.
search
(
istr
);
if
m
:
repl
=
"*"
+
m
.
group
()[
2
:
-
3
]
+
"*)("
;
istr
=
re_indirect
.
sub
(
repl
,
istr
);
repl
=
"*"
+
m
.
group
()[
2
:
-
3
]
+
"*)("
;
istr
=
re_indirect
.
sub
(
repl
,
istr
);
else
:
m
=
re_2
.
search
(
istr
)
if
m
:
...
...
@@ -125,72 +126,72 @@ def resolveIndirection(istr, varHash):
return
istr
;
def
removePtrCast
(
str
ing
):
def
removePtrCast
(
str
):
re_ptrCast
=
re
.
compile
(
'
\
([^
\
(]+
\
*
\
)'
);
str
ing
=
re_ptrCast
.
sub
(
""
,
string
);
return
str
ing
;
str
=
re_ptrCast
.
sub
(
""
,
str
);
return
str
;
def
resolvePtrMath
(
str
ing
,
ptrList
,
broad
=
0
):
def
resolvePtrMath
(
str
,
ptrList
,
broad
=
0
):
re_var
=
re
.
compile
(
'([
\
w
\
.]+)'
)
#possible variable
re_fn
=
re
.
compile
(
'(
\
w+
\
s*
\
(.*
\
))'
);
#function call ... not to be modified
re_word
=
re
.
compile
(
'
\
w+'
);
m
=
re_fn
.
split
(
str
ing
);
str
ing
=
""
;
m
=
re_fn
.
split
(
str
);
str
=
""
;
for
i
in
range
(
len
(
m
)):
flag
=
1
;
if
i
%
2
==
1
:
flag
=
0
;
m1
=
re_word
.
match
(
m
[
i
]);
if
not
(
m1
.
group
()
in
keywords
.
keywords
):
string
=
string
+
m
[
i
];
else
:
flag
=
1
;
flag
=
0
;
m1
=
re_word
.
match
(
m
[
i
]);
if
not
(
m1
.
group
()
in
keywords
.
keywords
):
str
=
str
+
m
[
i
];
else
:
flag
=
1
;
if
flag
:
if
broad
or
(
re
.
search
(
'
\
S+
\
s+
\
S+'
,
m
[
i
])):
a
=
re_var
.
split
(
m
[
i
]);
for
j
in
range
(
len
(
a
)):
# constant
if
(
j
%
2
==
1
)
and
a
[
j
]
.
isdigit
():
# if negative, make sure it is properly sign extended
if
int
(
a
[
j
])
>=
2
**
(
TARGET_BITWIDTH
-
1
):
string
=
string
+
"(int)"
# ptr variable
elif
(
j
%
2
==
1
)
and
a
[
j
]
in
ptrList
:
# remove address-of operator
addr
=
0
cast
=
1
re_str
=
re
.
compile
(
'(&)'
)
strTemp
=
re_str
.
split
(
string
);
if
len
(
strTemp
)
>
1
and
not
strTemp
[
-
1
]
.
strip
():
addr
=
1
string
=
''
.
join
(
strTemp
[:
-
2
])
# remove unnecessary (and wrong) pointer type cast
re_str
=
re
.
compile
(
'(
\
([
\
w
\
s]+
\
*?
\
s*
\
))'
)
strTemp
=
re_str
.
split
(
string
)
if
len
(
strTemp
)
>
1
and
not
strTemp
[
-
1
]
.
strip
():
if
strTemp
[
-
2
]
.
find
(
'*'
)
>=
0
:
cast
=
0
else
:
string
=
''
.
join
(
strTemp
[:
-
2
])
# if it was plain array access, don't cast
if
j
+
1
<
len
(
a
)
and
a
[
j
+
1
]
.
strip
()
.
startswith
(
'['
):
if
not
addr
:
cast
=
0
# if it was a struct member access, don't cast
if
j
+
1
<
len
(
a
)
and
a
[
j
+
1
]
.
strip
()
.
startswith
(
'->'
):
if
not
addr
:
cast
=
0
# likewise if the pointer is immediately dereferenced
if
string
.
strip
()
.
endswith
(
'*'
):
cast
=
0
# create properly casted pointer-taking operation
if
cast
:
string
=
string
+
UINTPTR_CAST
if
addr
:
string
=
string
+
"&"
string
=
string
+
a
[
j
];
else
:
string
=
string
+
m
[
i
];
return
str
ing
;
if
broad
or
(
re
.
search
(
'
\
S+
\
s+
\
S+'
,
m
[
i
])):
a
=
re_var
.
split
(
m
[
i
]);
for
j
in
range
(
len
(
a
)):
# constant
if
(
j
%
2
==
1
)
and
a
[
j
]
.
isdigit
():
# if negative, make sure it is properly sign extended
if
int
(
a
[
j
])
>=
2
**
(
TARGET_BITWIDTH
-
1
):
str
=
str
+
"(int)"
# ptr variable
elif
(
j
%
2
==
1
)
and
a
[
j
]
in
ptrList
:
# remove address-of operator
addr
=
0
cast
=
1
re_str
=
re
.
compile
(
'(&)'
)
strTemp
=
re_str
.
split
(
str
);
if
len
(
strTemp
)
>
1
and
not
strTemp
[
-
1
]
.
strip
():
addr
=
1
str
=
''
.
join
(
strTemp
[:
-
2
])
# remove unnecessary (and wrong) pointer type cast
re_str
=
re
.
compile
(
'(
\
([
\
w
\
s]+
\
*?
\
s*
\
))'
)
strTemp
=
re_str
.
split
(
str
)
if
len
(
strTemp
)
>
1
and
not
strTemp
[
-
1
]
.
strip
():
if
strTemp
[
-
2
]
.
find
(
'*'
)
>=
0
:
cast
=
0
else
:
str
=
''
.
join
(
strTemp
[:
-
2
])
# if it was plain array access, don't cast
if
j
+
1
<
len
(
a
)
and
a
[
j
+
1
]
.
strip
()
.
startswith
(
'['
):
if
not
addr
:
cast
=
0
# if it was a struct member access, don't cast
if
j
+
1
<
len
(
a
)
and
a
[
j
+
1
]
.
strip
()
.
startswith
(
'->'
):
if
not
addr
:
cast
=
0
# likewise if the pointer is immediately dereferenced
if
str
.
strip
()
.
endswith
(
'*'
):
cast
=
0
# create properly casted pointer-taking operation
if
cast
:
str
=
str
+
UINTPTR_CAST
if
addr
:
str
=
str
+
"&"
str
=
str
+
a
[
j
];
else
:
str
=
str
+
m
[
i
];
return
str
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment