Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
Pyston
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
Pyston
Commits
ac004e74
Commit
ac004e74
authored
Apr 22, 2014
by
Kevin Modzelewski
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
rangify the codebase
parent
4fa5406f
Changes
17
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
196 additions
and
191 deletions
+196
-191
src/analysis/fpc.h
src/analysis/fpc.h
+11
-11
src/analysis/function_analysis.cpp
src/analysis/function_analysis.cpp
+7
-8
src/analysis/scoping_analysis.cpp
src/analysis/scoping_analysis.cpp
+4
-6
src/analysis/type_analysis.cpp
src/analysis/type_analysis.cpp
+12
-12
src/asm_writing/icinfo.cpp
src/asm_writing/icinfo.cpp
+2
-3
src/codegen/codegen.cpp
src/codegen/codegen.cpp
+5
-6
src/codegen/irgen.cpp
src/codegen/irgen.cpp
+63
-62
src/codegen/irgen/irgenerator.cpp
src/codegen/irgen/irgenerator.cpp
+27
-25
src/codegen/irgen/irgenerator.h
src/codegen/irgen/irgenerator.h
+8
-7
src/codegen/llvm_interpreter.cpp
src/codegen/llvm_interpreter.cpp
+30
-26
src/codegen/opt/dead_allocs.cpp
src/codegen/opt/dead_allocs.cpp
+3
-0
src/codegen/opt/inliner.cpp
src/codegen/opt/inliner.cpp
+6
-5
src/codegen/patchpoints.cpp
src/codegen/patchpoints.cpp
+2
-3
src/core/stats.cpp
src/core/stats.cpp
+2
-2
src/runtime/dict.cpp
src/runtime/dict.cpp
+10
-10
src/runtime/inline/gc_runtime.cpp
src/runtime/inline/gc_runtime.cpp
+2
-2
src/runtime/objmodel.cpp
src/runtime/objmodel.cpp
+2
-3
No files found.
src/analysis/fpc.h
View file @
ac004e74
...
...
@@ -66,18 +66,18 @@ typename BBAnalyzer<T>::AllMap computeFixedPoint(CFG* cfg, const BBAnalyzer<T> &
}
Map
&
next
=
states
[
next_block
];
for
(
typename
Map
::
iterator
it
=
ending
.
begin
(),
end
=
ending
.
end
();
it
!=
end
;
++
it
)
{
if
(
next
.
count
(
it
->
first
)
==
0
)
{
for
(
auto
p
:
ending
)
{
if
(
next
.
count
(
p
.
first
)
==
0
)
{
changed
=
true
;
if
(
initial
)
{
next
[
it
->
first
]
=
it
->
second
;
next
[
p
.
first
]
=
p
.
second
;
}
else
{
next
[
it
->
first
]
=
analyzer
.
mergeBlank
(
it
->
second
);
next
[
p
.
first
]
=
analyzer
.
mergeBlank
(
p
.
second
);
}
}
else
{
T
&
next_elt
=
next
[
it
->
first
];
T
&
next_elt
=
next
[
p
.
first
];
T
new_elt
=
analyzer
.
merge
(
it
->
second
,
next_elt
);
T
new_elt
=
analyzer
.
merge
(
p
.
second
,
next_elt
);
if
(
next_elt
!=
new_elt
)
{
next_elt
=
new_elt
;
changed
=
true
;
...
...
@@ -85,13 +85,13 @@ typename BBAnalyzer<T>::AllMap computeFixedPoint(CFG* cfg, const BBAnalyzer<T> &
}
}
for
(
typename
Map
::
iterator
it
=
next
.
begin
(),
end
=
ending
.
end
();
it
!=
end
;
++
it
)
{
if
(
ending
.
count
(
it
->
first
))
for
(
auto
p
:
ending
)
{
if
(
ending
.
count
(
p
.
first
))
continue
;
T
next_elt
=
analyzer
.
mergeBlank
(
it
->
second
);
if
(
next_elt
!=
it
->
second
)
{
next
[
it
->
first
]
=
next_elt
;
T
next_elt
=
analyzer
.
mergeBlank
(
p
.
second
);
if
(
next_elt
!=
p
.
second
)
{
next
[
p
.
first
]
=
next_elt
;
changed
=
true
;
}
}
...
...
src/analysis/function_analysis.cpp
View file @
ac004e74
...
...
@@ -240,18 +240,17 @@ void DefinednessBBAnalyzer::processBB(Map &starting, CFGBlock *block) const {
DefinednessAnalysis
::
DefinednessAnalysis
(
AST_arguments
*
args
,
CFG
*
cfg
,
ScopeInfo
*
scope_info
)
:
scope_info
(
scope_info
)
{
results
=
computeFixedPoint
(
cfg
,
DefinednessBBAnalyzer
(
args
),
false
);
for
(
std
::
unordered_map
<
CFGBlock
*
,
std
::
unordered_map
<
std
::
string
,
DefinitionLevel
>
>::
iterator
it
=
results
.
begin
(),
end
=
results
.
end
();
it
!=
end
;
++
it
)
{
for
(
auto
p
:
results
)
{
RequiredSet
required
;
for
(
std
::
unordered_map
<
std
::
string
,
DefinitionLevel
>::
iterator
it2
=
it
->
second
.
begin
(),
end2
=
it
->
second
.
end
();
for
(
std
::
unordered_map
<
std
::
string
,
DefinitionLevel
>::
iterator
it2
=
p
.
second
.
begin
(),
end2
=
p
.
second
.
end
();
it2
!=
end2
;
++
it2
)
{
if
(
scope_info
->
refersToGlobal
(
it2
->
first
))
continue
;
//printf("%d %s %d\n",
it->
first->idx, it2->first.c_str(), it2->second);
//printf("%d %s %d\n",
p.
first->idx, it2->first.c_str(), it2->second);
required
.
insert
(
it2
->
first
);
}
defined
.
insert
(
make_pair
(
it
->
first
,
required
));
defined
.
insert
(
make_pair
(
p
.
first
,
required
));
}
}
...
...
@@ -278,9 +277,9 @@ PhiAnalysis::PhiAnalysis(AST_arguments* args, CFG* cfg, LivenessAnalysis *livene
const
RequiredSet
&
defined
=
definedness
.
getDefinedNamesAt
(
block
);
if
(
defined
.
size
())
assert
(
block
->
predecessors
.
size
());
for
(
RequiredSet
::
const_iterator
it
=
defined
.
begin
(),
end
=
defined
.
end
();
it
!=
end
;
++
it
)
{
if
(
liveness
->
isLiveAtEnd
(
*
it
,
block
->
predecessors
[
0
]))
{
required
.
insert
(
*
it
);
for
(
auto
s
:
defined
)
{
if
(
liveness
->
isLiveAtEnd
(
s
,
block
->
predecessors
[
0
]))
{
required
.
insert
(
s
);
}
}
...
...
src/analysis/scoping_analysis.cpp
View file @
ac004e74
...
...
@@ -256,9 +256,8 @@ static std::vector<ScopingAnalysis::ScopeNameUsage*> sortNameUsages(ScopingAnaly
std
::
vector
<
ScopingAnalysis
::
ScopeNameUsage
*>
rtn
;
std
::
unordered_set
<
ScopingAnalysis
::
ScopeNameUsage
*>
added
;
for
(
ScopingAnalysis
::
NameUsageMap
::
iterator
it
=
usages
->
begin
(),
end
=
usages
->
end
();
it
!=
end
;
++
it
)
{
ScopingAnalysis
::
ScopeNameUsage
*
usage
=
it
->
second
;
for
(
auto
p
:
*
usages
)
{
ScopingAnalysis
::
ScopeNameUsage
*
usage
=
p
.
second
;
std
::
vector
<
ScopingAnalysis
::
ScopeNameUsage
*>
traversed
;
...
...
@@ -282,9 +281,8 @@ void ScopingAnalysis::processNameUsages(ScopingAnalysis::NameUsageMap* usages) {
typedef
ScopeNameUsage
::
StrSet
StrSet
;
// Resolve name lookups:
for
(
ScopingAnalysis
::
NameUsageMap
::
iterator
it
=
usages
->
begin
(),
end
=
usages
->
end
();
it
!=
end
;
++
it
)
{
ScopeNameUsage
*
usage
=
it
->
second
;
for
(
auto
p
:
*
usages
)
{
ScopeNameUsage
*
usage
=
p
.
second
;
for
(
StrSet
::
iterator
it2
=
usage
->
read
.
begin
(),
end2
=
usage
->
read
.
end
();
it2
!=
end2
;
++
it2
)
{
if
(
usage
->
forced_globals
.
count
(
*
it2
))
...
...
src/analysis/type_analysis.cpp
View file @
ac004e74
...
...
@@ -566,9 +566,9 @@ class PropagatingTypeAnalysis : public TypeAnalysis {
if
(
VERBOSITY
(
"types"
)
>=
2
)
{
printf
(
"before:
\n
"
);
TypeMap
&
starting
=
starting_types
[
block_id
];
for
(
TypeMap
::
iterator
it
=
starting
.
begin
(),
end
=
starting
.
end
();
it
!=
end
;
++
it
)
{
ASSERT
(
it
->
second
,
"%s"
,
it
->
first
.
c_str
());
printf
(
"%s: %s
\n
"
,
it
->
first
.
c_str
(),
it
->
second
->
debugName
().
c_str
());
for
(
auto
p
:
starting
)
{
ASSERT
(
p
.
second
,
"%s"
,
p
.
first
.
c_str
());
printf
(
"%s: %s
\n
"
,
p
.
first
.
c_str
(),
p
.
second
->
debugName
().
c_str
());
}
}
...
...
@@ -577,14 +577,14 @@ class PropagatingTypeAnalysis : public TypeAnalysis {
if
(
VERBOSITY
(
"types"
)
>=
2
)
{
printf
(
"before (after):
\n
"
);
TypeMap
&
starting
=
starting_types
[
block_id
];
for
(
TypeMap
::
iterator
it
=
starting
.
begin
(),
end
=
starting
.
end
();
it
!=
end
;
++
it
)
{
ASSERT
(
it
->
second
,
"%s"
,
it
->
first
.
c_str
());
printf
(
"%s: %s
\n
"
,
it
->
first
.
c_str
(),
it
->
second
->
debugName
().
c_str
());
for
(
auto
p
:
starting
)
{
ASSERT
(
p
.
second
,
"%s"
,
p
.
first
.
c_str
());
printf
(
"%s: %s
\n
"
,
p
.
first
.
c_str
(),
p
.
second
->
debugName
().
c_str
());
}
printf
(
"after:
\n
"
);
for
(
TypeMap
::
iterator
it
=
ending
.
begin
(),
end
=
ending
.
end
();
it
!=
end
;
++
it
)
{
ASSERT
(
it
->
second
,
"%s"
,
it
->
first
.
c_str
());
printf
(
"%s: %s
\n
"
,
it
->
first
.
c_str
(),
it
->
second
->
debugName
().
c_str
());
for
(
auto
p
:
ending
)
{
ASSERT
(
p
.
second
,
"%s"
,
p
.
first
.
c_str
());
printf
(
"%s: %s
\n
"
,
p
.
first
.
c_str
(),
p
.
second
->
debugName
().
c_str
());
}
}
...
...
@@ -604,9 +604,9 @@ class PropagatingTypeAnalysis : public TypeAnalysis {
CFGBlock
*
b
=
cfg
->
blocks
[
i
];
TypeMap
&
starting
=
starting_types
[
i
];
for
(
TypeMap
::
iterator
it
=
starting
.
begin
(),
end
=
starting
.
end
();
it
!=
end
;
++
it
)
{
ASSERT
(
it
->
second
,
"%s"
,
it
->
first
.
c_str
());
printf
(
"%s: %s
\n
"
,
it
->
first
.
c_str
(),
it
->
second
->
debugName
().
c_str
());
for
(
auto
p
:
starting
)
{
ASSERT
(
p
.
second
,
"%s"
,
p
.
first
.
c_str
());
printf
(
"%s: %s
\n
"
,
p
.
first
.
c_str
(),
p
.
second
->
debugName
().
c_str
());
}
}
}
...
...
src/asm_writing/icinfo.cpp
View file @
ac004e74
...
...
@@ -42,9 +42,8 @@ void ICInvalidator::addDependent(ICSlotInfo* entry_info) {
void
ICInvalidator
::
invalidateAll
()
{
cur_version
++
;
for
(
std
::
unordered_set
<
ICSlotInfo
*>::
iterator
it
=
dependents
.
begin
(),
end
=
dependents
.
end
();
it
!=
end
;
++
it
)
{
(
*
it
)
->
clear
();
for
(
ICSlotInfo
*
slot
:
dependents
)
{
slot
->
clear
();
}
dependents
.
clear
();
}
...
...
src/codegen/codegen.cpp
View file @
ac004e74
...
...
@@ -47,17 +47,16 @@ void FunctionAddressRegistry::dumpPerfMap() {
char
buf
[
80
];
snprintf
(
buf
,
80
,
"/tmp/perf-%d.map"
,
getpid
());
FILE
*
f
=
fopen
(
buf
,
"w"
);
for
(
FuncMap
::
iterator
it
=
functions
.
begin
(),
end
=
functions
.
end
();
it
!=
end
;
++
it
)
{
const
FuncInfo
&
info
=
it
->
second
;
fprintf
(
f
,
"%lx %x %s
\n
"
,
(
uintptr_t
)
it
->
first
,
info
.
length
,
info
.
name
.
c_str
());
for
(
auto
p
:
functions
)
{
const
FuncInfo
&
info
=
p
.
second
;
fprintf
(
f
,
"%lx %x %s
\n
"
,
(
uintptr_t
)
p
.
first
,
info
.
length
,
info
.
name
.
c_str
());
if
(
info
.
length
>
0
)
{
fprintf
(
index_f
,
"%lx %s
\n
"
,
(
uintptr_t
)
it
->
first
,
info
.
name
.
c_str
());
fprintf
(
index_f
,
"%lx %s
\n
"
,
(
uintptr_t
)
p
.
first
,
info
.
name
.
c_str
());
FILE
*
data_f
=
fopen
((
out_path
+
"/"
+
info
.
name
).
c_str
(),
"wb"
);
int
written
=
fwrite
((
void
*
)
it
->
first
,
1
,
info
.
length
,
data_f
);
int
written
=
fwrite
((
void
*
)
p
.
first
,
1
,
info
.
length
,
data_f
);
assert
(
written
==
info
.
length
);
fclose
(
data_f
);
}
...
...
src/codegen/irgen.cpp
View file @
ac004e74
This diff is collapsed.
Click to expand it.
src/codegen/irgen/irgenerator.cpp
View file @
ac004e74
...
...
@@ -78,16 +78,17 @@ GuardList::ExprTypeGuard::ExprTypeGuard(CFGBlock *cfg_block, llvm::BranchInst* b
cfg_block
(
cfg_block
),
branch
(
branch
),
ast_node
(
ast_node
)
{
DupCache
cache
;
this
->
val
=
val
->
dup
(
cache
);
for
(
SymbolTable
::
const_iterator
it
=
st
.
begin
(),
end
=
st
.
end
();
it
!=
end
;
++
it
)
{
this
->
st
[
it
->
first
]
=
it
->
second
->
dup
(
cache
);
for
(
auto
p
:
st
)
{
this
->
st
[
p
.
first
]
=
p
.
second
->
dup
(
cache
);
}
}
GuardList
::
BlockEntryGuard
::
BlockEntryGuard
(
CFGBlock
*
cfg_block
,
llvm
::
BranchInst
*
branch
,
const
SymbolTable
&
symbol_table
)
:
cfg_block
(
cfg_block
),
branch
(
branch
)
{
DupCache
cache
;
for
(
SymbolTable
::
const_iterator
it
=
symbol_table
.
begin
(),
end
=
symbol_table
.
end
();
it
!=
end
;
++
it
)
{
this
->
symbol_table
[
it
->
first
]
=
it
->
second
->
dup
(
cache
);
for
(
auto
p
:
symbol_table
)
{
this
->
symbol_table
[
p
.
first
]
=
p
.
second
->
dup
(
cache
);
}
}
...
...
@@ -925,34 +926,34 @@ class IRGeneratorImpl : public IRGenerator {
llvm
::
BasicBlock
*
ramp_block
=
llvm
::
BasicBlock
::
Create
(
g
.
context
,
"deopt_ramp"
,
irstate
->
getLLVMFunction
());
llvm
::
BasicBlock
*
join_block
=
llvm
::
BasicBlock
::
Create
(
g
.
context
,
"deopt_join"
,
irstate
->
getLLVMFunction
());
SymbolTable
joined_st
;
for
(
SymbolTable
::
iterator
it
=
guard
->
st
.
begin
(),
end
=
guard
->
st
.
end
();
it
!=
end
;
++
i
t
)
{
//if (VERBOSITY("irgen") >= 1) printf("merging %s\n",
it->
first.c_str());
CompilerVariable
*
curval
=
symbol_table
[
it
->
first
];
for
(
auto
p
:
guard
->
s
t
)
{
//if (VERBOSITY("irgen") >= 1) printf("merging %s\n",
p.
first.c_str());
CompilerVariable
*
curval
=
symbol_table
[
p
.
first
];
// I'm not sure this is necessary or even correct:
//ASSERT(curval->getVrefs() ==
it->second->getVrefs(), "%s %d %d", it->first.c_str(), curval->getVrefs(), it->
second->getVrefs());
//ASSERT(curval->getVrefs() ==
p.second->getVrefs(), "%s %d %d", p.first.c_str(), curval->getVrefs(), p.
second->getVrefs());
ConcreteCompilerType
*
merged_type
=
curval
->
getConcreteType
();
emitter
.
getBuilder
()
->
SetInsertPoint
(
ramp_block
);
ConcreteCompilerVariable
*
converted1
=
it
->
second
->
makeConverted
(
emitter
,
merged_type
);
it
->
second
->
decvref
(
emitter
);
// for makeconverted
//guard->st[
it->
first] = converted;
//
it->
second->decvref(emitter); // for the replaced version
ConcreteCompilerVariable
*
converted1
=
p
.
second
->
makeConverted
(
emitter
,
merged_type
);
p
.
second
->
decvref
(
emitter
);
// for makeconverted
//guard->st[
p.
first] = converted;
//
p.
second->decvref(emitter); // for the replaced version
emitter
.
getBuilder
()
->
SetInsertPoint
(
curblock
);
ConcreteCompilerVariable
*
converted2
=
curval
->
makeConverted
(
emitter
,
merged_type
);
curval
->
decvref
(
emitter
);
// for makeconverted
//symbol_table[
it->
first] = converted;
//symbol_table[
p.
first] = converted;
//curval->decvref(emitter); // for the replaced version
if
(
converted1
->
getValue
()
==
converted2
->
getValue
())
{
joined_st
[
it
->
first
]
=
new
ConcreteCompilerVariable
(
merged_type
,
converted1
->
getValue
(),
true
);
joined_st
[
p
.
first
]
=
new
ConcreteCompilerVariable
(
merged_type
,
converted1
->
getValue
(),
true
);
}
else
{
emitter
.
getBuilder
()
->
SetInsertPoint
(
join_block
);
llvm
::
PHINode
*
phi
=
emitter
.
getBuilder
()
->
CreatePHI
(
merged_type
->
llvmType
(),
2
,
it
->
first
);
llvm
::
PHINode
*
phi
=
emitter
.
getBuilder
()
->
CreatePHI
(
merged_type
->
llvmType
(),
2
,
p
.
first
);
phi
->
addIncoming
(
converted1
->
getValue
(),
ramp_block
);
phi
->
addIncoming
(
converted2
->
getValue
(),
curblock
);
joined_st
[
it
->
first
]
=
new
ConcreteCompilerVariable
(
merged_type
,
phi
,
true
);
joined_st
[
p
.
first
]
=
new
ConcreteCompilerVariable
(
merged_type
,
phi
,
true
);
}
// TODO free dead Variable objects!
...
...
@@ -1374,17 +1375,18 @@ class IRGeneratorImpl : public IRGenerator {
}
}
int
i
=
0
;
for
(
SortedSymbolTable
::
iterator
it
=
sorted_symbol_table
.
begin
(),
end
=
sorted_symbol_table
.
end
();
it
!=
end
;
++
it
,
++
i
)
{
int
arg_num
=
-
1
;
for
(
auto
p
:
sorted_symbol_table
)
{
arg_num
++
;
// I don't think this can fail, but if it can we should filter out dead symbols before
// passing them on:
ASSERT
(
startswith
(
it
->
first
,
"!is_defined"
)
||
irstate
->
getSourceInfo
()
->
liveness
->
isLiveAtEnd
(
it
->
first
,
myblock
),
"%d %s"
,
myblock
->
idx
,
it
->
first
.
c_str
());
ASSERT
(
startswith
(
p
.
first
,
"!is_defined"
)
||
irstate
->
getSourceInfo
()
->
liveness
->
isLiveAtEnd
(
p
.
first
,
myblock
),
"%d %s"
,
myblock
->
idx
,
p
.
first
.
c_str
());
// This line can never get hit right now since we unnecessarily force every variable to be concrete
// for a loop, since we generate all potential phis:
ASSERT
(
it
->
second
->
getType
()
==
it
->
second
->
getConcreteType
(),
"trying to pass through %s
\n
"
,
it
->
second
->
getType
()
->
debugName
().
c_str
());
ASSERT
(
p
.
second
->
getType
()
==
p
.
second
->
getConcreteType
(),
"trying to pass through %s
\n
"
,
p
.
second
->
getType
()
->
debugName
().
c_str
());
ConcreteCompilerVariable
*
var
=
it
->
second
->
makeConverted
(
emitter
,
it
->
second
->
getConcreteType
());
ConcreteCompilerVariable
*
var
=
p
.
second
->
makeConverted
(
emitter
,
p
.
second
->
getConcreteType
());
converted_args
.
push_back
(
var
);
assert
(
var
->
getType
()
!=
BOXED_INT
&&
"should probably unbox it, but why is it boxed in the first place?"
);
...
...
@@ -1392,16 +1394,16 @@ class IRGeneratorImpl : public IRGenerator {
// This line can never get hit right now for the same reason that the variables must already be concrete,
// because we're over-generating phis.
ASSERT
(
var
->
isGrabbed
(),
"%s"
,
it
->
first
.
c_str
());
ASSERT
(
var
->
isGrabbed
(),
"%s"
,
p
.
first
.
c_str
());
//var->ensureGrabbed(emitter);
llvm
::
Value
*
val
=
var
->
getValue
();
if
(
i
<
3
)
{
if
(
arg_num
<
3
)
{
llvm_args
.
push_back
(
val
);
llvm_arg_types
.
push_back
(
val
->
getType
());
}
else
{
llvm
::
Value
*
ptr
=
emitter
.
getBuilder
()
->
CreateConstGEP1_32
(
arg_array
,
i
-
3
);
llvm
::
Value
*
ptr
=
emitter
.
getBuilder
()
->
CreateConstGEP1_32
(
arg_array
,
arg_num
-
3
);
if
(
var
->
getType
()
==
INT
)
{
val
=
emitter
.
getBuilder
()
->
CreateIntToPtr
(
val
,
g
.
llvm_value_type_ptr
);
...
...
@@ -1415,7 +1417,7 @@ class IRGeneratorImpl : public IRGenerator {
emitter
.
getBuilder
()
->
CreateStore
(
val
,
ptr
);
}
ConcreteCompilerType
*
&
t
=
exit
->
entry
->
args
[
it
->
first
];
ConcreteCompilerType
*
&
t
=
exit
->
entry
->
args
[
p
.
first
];
if
(
t
==
NULL
)
t
=
var
->
getType
();
else
...
...
src/codegen/irgen/irgenerator.h
View file @
ac004e74
...
...
@@ -17,6 +17,8 @@
#include <map>
#include "llvm/ADT/iterator_range.h"
#include "core/types.h"
namespace
llvm
{
...
...
@@ -114,15 +116,14 @@ class GuardList {
private:
std
::
unordered_map
<
AST_expr
*
,
ExprTypeGuard
*>
expr_type_guards
;
std
::
unordered_map
<
CFGBlock
*
,
std
::
vector
<
BlockEntryGuard
*>
>
block_begin_guards
;
//typedef std::unordered_map<AST_expr*, ExprTypeGuard*>::iterator expr_type_guard_iterator;
//typedef std::unordered_map<AST_expr*, ExprTypeGuard*>::const_iterator expr_type_guard_const_iterator;
typedef
decltype
(
expr_type_guards
)
::
iterator
expr_type_guard_iterator
;
typedef
decltype
(
expr_type_guards
)
::
const_iterator
expr_type_guard_const_iterator
;
public:
typedef
std
::
unordered_map
<
AST_expr
*
,
ExprTypeGuard
*>::
iterator
expr_type_guard_iterator
;
typedef
std
::
unordered_map
<
AST_expr
*
,
ExprTypeGuard
*>::
const_iterator
expr_type_guard_const_iterator
;
expr_type_guard_iterator
after_begin
()
{
return
expr_type_guards
.
begin
();
}
expr_type_guard_iterator
after_end
()
{
return
expr_type_guards
.
end
();
llvm
::
iterator_range
<
expr_type_guard_iterator
>
exprGuards
()
{
return
llvm
::
iterator_range
<
expr_type_guard_iterator
>
(
expr_type_guards
.
begin
(),
expr_type_guards
.
end
());
}
void
getBlocksWithGuards
(
std
::
unordered_set
<
CFGBlock
*>
&
add_to
)
{
...
...
src/codegen/llvm_interpreter.cpp
View file @
ac004e74
...
...
@@ -256,16 +256,18 @@ Box* interpretFunction(llvm::Function *f, int nargs, Box* arg1, Box* arg2, Box*
interpreter_roots
[
frame_ptr
]
=
&
symbols
;
UnregisterHelper
helper
(
frame_ptr
);
int
i
=
0
;
for
(
llvm
::
Function
::
arg_iterator
AI
=
f
->
arg_begin
(),
end
=
f
->
arg_end
();
AI
!=
end
;
AI
++
,
i
++
)
{
if
(
i
==
0
)
symbols
.
insert
(
std
::
make_pair
(
static_cast
<
llvm
::
Value
*>
(
&
(
*
AI
)),
Val
(
arg1
)));
else
if
(
i
==
1
)
symbols
.
insert
(
std
::
make_pair
(
static_cast
<
llvm
::
Value
*>
(
&
(
*
AI
)),
Val
(
arg2
)));
else
if
(
i
==
2
)
symbols
.
insert
(
std
::
make_pair
(
static_cast
<
llvm
::
Value
*>
(
&
(
*
AI
)),
Val
(
arg3
)));
int
arg_num
=
-
1
;
for
(
llvm
::
Argument
&
arg
:
f
->
args
())
{
arg_num
++
;
if
(
arg_num
==
0
)
symbols
.
insert
(
std
::
make_pair
(
static_cast
<
llvm
::
Value
*>
(
&
arg
),
Val
(
arg1
)));
else
if
(
arg_num
==
1
)
symbols
.
insert
(
std
::
make_pair
(
static_cast
<
llvm
::
Value
*>
(
&
arg
),
Val
(
arg2
)));
else
if
(
arg_num
==
2
)
symbols
.
insert
(
std
::
make_pair
(
static_cast
<
llvm
::
Value
*>
(
&
arg
),
Val
(
arg3
)));
else
{
assert
(
i
==
3
);
assert
(
arg_num
==
3
);
assert
(
f
->
getArgumentList
().
size
()
==
4
);
assert
(
f
->
getArgumentList
().
back
().
getType
()
==
g
.
llvm_value_type_ptr
->
getPointerTo
());
symbols
.
insert
(
std
::
make_pair
(
static_cast
<
llvm
::
Value
*>
(
&
(
*
AI
)
),
Val
((
int64_t
)
args
)));
symbols
.
insert
(
std
::
make_pair
(
static_cast
<
llvm
::
Value
*>
(
&
arg
),
Val
((
int64_t
)
args
)));
//printf("loading %%4 with %p\n", (void*)args);
break
;
}
...
...
@@ -276,16 +278,18 @@ Box* interpretFunction(llvm::Function *f, int nargs, Box* arg1, Box* arg2, Box*
while
(
true
)
{
for
(
llvm
::
BasicBlock
::
iterator
it
=
curblock
->
begin
(),
end
=
curblock
->
end
();
it
!=
end
;
++
it
)
{
for
(
llvm
::
Instruction
&
_inst
:
*
curblock
)
{
llvm
::
Instruction
*
inst
=
&
_inst
;
if
(
VERBOSITY
(
"interpreter"
)
>=
2
)
{
printf
(
"executing in %s: "
,
f
->
getName
().
data
());
fflush
(
stdout
);
it
->
dump
();
i
ns
t
->
dump
();
//f->dump();
}
#define SET(v) set(symbols, it, (v))
if
(
llvm
::
LoadInst
*
li
=
llvm
::
dyn_cast
<
llvm
::
LoadInst
>
(
it
))
{
#define SET(v) set(symbols, i
ns
t, (v))
if
(
llvm
::
LoadInst
*
li
=
llvm
::
dyn_cast
<
llvm
::
LoadInst
>
(
i
ns
t
))
{
llvm
::
Value
*
ptr
=
li
->
getOperand
(
0
);
Val
v
=
fetch
(
ptr
,
dl
,
symbols
);
//printf("loading from %p\n", v.o);
...
...
@@ -302,7 +306,7 @@ Box* interpretFunction(llvm::Function *f, int nargs, Box* arg1, Box* arg2, Box*
li
->
dump
();
RELEASE_ASSERT
(
0
,
""
);
}
}
else
if
(
llvm
::
StoreInst
*
si
=
llvm
::
dyn_cast
<
llvm
::
StoreInst
>
(
it
))
{
}
else
if
(
llvm
::
StoreInst
*
si
=
llvm
::
dyn_cast
<
llvm
::
StoreInst
>
(
i
ns
t
))
{
llvm
::
Value
*
val
=
si
->
getOperand
(
0
);
llvm
::
Value
*
ptr
=
si
->
getOperand
(
1
);
Val
v
=
fetch
(
val
,
dl
,
symbols
);
...
...
@@ -320,7 +324,7 @@ Box* interpretFunction(llvm::Function *f, int nargs, Box* arg1, Box* arg2, Box*
si
->
dump
();
RELEASE_ASSERT
(
0
,
""
);
}
}
else
if
(
llvm
::
CmpInst
*
ci
=
llvm
::
dyn_cast
<
llvm
::
CmpInst
>
(
it
))
{
}
else
if
(
llvm
::
CmpInst
*
ci
=
llvm
::
dyn_cast
<
llvm
::
CmpInst
>
(
i
ns
t
))
{
assert
(
ci
->
getType
()
==
g
.
i1
);
Val
a0
=
fetch
(
ci
->
getOperand
(
0
),
dl
,
symbols
);
...
...
@@ -368,7 +372,7 @@ Box* interpretFunction(llvm::Function *f, int nargs, Box* arg1, Box* arg2, Box*
RELEASE_ASSERT
(
0
,
""
);
}
continue
;
}
else
if
(
llvm
::
BinaryOperator
*
bo
=
llvm
::
dyn_cast
<
llvm
::
BinaryOperator
>
(
it
))
{
}
else
if
(
llvm
::
BinaryOperator
*
bo
=
llvm
::
dyn_cast
<
llvm
::
BinaryOperator
>
(
i
ns
t
))
{
if
(
bo
->
getOperand
(
0
)
->
getType
()
==
g
.
i64
||
bo
->
getOperand
(
0
)
->
getType
()
==
g
.
i1
)
{
//assert(bo->getOperand(0)->getType() == g.i64);
//assert(bo->getOperand(1)->getType() == g.i64);
...
...
@@ -432,7 +436,7 @@ Box* interpretFunction(llvm::Function *f, int nargs, Box* arg1, Box* arg2, Box*
bo
->
dump
();
RELEASE_ASSERT
(
0
,
""
);
}
}
else
if
(
llvm
::
GetElementPtrInst
*
gep
=
llvm
::
dyn_cast
<
llvm
::
GetElementPtrInst
>
(
it
))
{
}
else
if
(
llvm
::
GetElementPtrInst
*
gep
=
llvm
::
dyn_cast
<
llvm
::
GetElementPtrInst
>
(
i
ns
t
))
{
int64_t
base
=
fetch
(
gep
->
getPointerOperand
(),
dl
,
symbols
).
n
;
llvm
::
User
::
value_op_iterator
begin
=
gep
->
value_op_begin
();
...
...
@@ -444,26 +448,26 @@ Box* interpretFunction(llvm::Function *f, int nargs, Box* arg1, Box* arg2, Box*
//printf("offset for inst: %ld (base is %lx)\n", offset, base);
SET
(
base
+
offset
);
continue
;
}
else
if
(
llvm
::
AllocaInst
*
al
=
llvm
::
dyn_cast
<
llvm
::
AllocaInst
>
(
it
))
{
}
else
if
(
llvm
::
AllocaInst
*
al
=
llvm
::
dyn_cast
<
llvm
::
AllocaInst
>
(
i
ns
t
))
{
int
size
=
fetch
(
al
->
getArraySize
(),
dl
,
symbols
).
n
*
width
(
al
->
getAllocatedType
(),
dl
);
void
*
ptr
=
alloca
(
size
);
//void* ptr = malloc(size);
//printf("alloca()'d at %p\n", ptr);
SET
((
int64_t
)
ptr
);
continue
;
}
else
if
(
llvm
::
SIToFPInst
*
si
=
llvm
::
dyn_cast
<
llvm
::
SIToFPInst
>
(
it
))
{
}
else
if
(
llvm
::
SIToFPInst
*
si
=
llvm
::
dyn_cast
<
llvm
::
SIToFPInst
>
(
i
ns
t
))
{
assert
(
width
(
si
->
getOperand
(
0
),
dl
)
==
8
);
SET
((
double
)
fetch
(
si
->
getOperand
(
0
),
dl
,
symbols
).
n
);
continue
;
}
else
if
(
llvm
::
BitCastInst
*
bc
=
llvm
::
dyn_cast
<
llvm
::
BitCastInst
>
(
it
))
{
}
else
if
(
llvm
::
BitCastInst
*
bc
=
llvm
::
dyn_cast
<
llvm
::
BitCastInst
>
(
i
ns
t
))
{
assert
(
width
(
bc
->
getOperand
(
0
),
dl
)
==
8
);
SET
(
fetch
(
bc
->
getOperand
(
0
),
dl
,
symbols
));
continue
;
}
else
if
(
llvm
::
IntToPtrInst
*
bc
=
llvm
::
dyn_cast
<
llvm
::
IntToPtrInst
>
(
it
))
{
}
else
if
(
llvm
::
IntToPtrInst
*
bc
=
llvm
::
dyn_cast
<
llvm
::
IntToPtrInst
>
(
i
ns
t
))
{
assert
(
width
(
bc
->
getOperand
(
0
),
dl
)
==
8
);
SET
(
fetch
(
bc
->
getOperand
(
0
),
dl
,
symbols
));
continue
;
}
else
if
(
llvm
::
CallInst
*
ci
=
llvm
::
dyn_cast
<
llvm
::
CallInst
>
(
it
))
{
}
else
if
(
llvm
::
CallInst
*
ci
=
llvm
::
dyn_cast
<
llvm
::
CallInst
>
(
i
ns
t
))
{
void
*
f
;
int
arg_start
;
if
(
ci
->
getCalledFunction
()
&&
(
ci
->
getCalledFunction
()
->
getName
()
==
"llvm.experimental.patchpoint.void"
||
ci
->
getCalledFunction
()
->
getName
()
==
"llvm.experimental.patchpoint.i64"
))
{
...
...
@@ -568,7 +572,7 @@ Box* interpretFunction(llvm::Function *f, int nargs, Box* arg1, Box* arg2, Box*
r
=
reinterpret_cast
<
int64_t
(
*
)(
int64_t
,
int64_t
,
int64_t
,
int64_t
,
int64_t
,
int64_t
,
int64_t
,
int64_t
)
>
(
f
)(
args
[
0
].
n
,
args
[
1
].
n
,
args
[
2
].
n
,
args
[
3
].
n
,
args
[
4
].
n
,
args
[
5
].
n
,
args
[
6
].
n
,
args
[
7
].
n
);
break
;
default:
it
->
dump
();
i
ns
t
->
dump
();
RELEASE_ASSERT
(
0
,
"%d"
,
mask
);
break
;
}
...
...
@@ -580,7 +584,7 @@ Box* interpretFunction(llvm::Function *f, int nargs, Box* arg1, Box* arg2, Box*
_t
.
restart
(
"to interpret"
,
10000000
);
#endif
continue
;
}
else
if
(
llvm
::
SelectInst
*
si
=
llvm
::
dyn_cast
<
llvm
::
SelectInst
>
(
it
))
{
}
else
if
(
llvm
::
SelectInst
*
si
=
llvm
::
dyn_cast
<
llvm
::
SelectInst
>
(
i
ns
t
))
{
Val
test
=
fetch
(
si
->
getCondition
(),
dl
,
symbols
);
Val
vt
=
fetch
(
si
->
getTrueValue
(),
dl
,
symbols
);
Val
vf
=
fetch
(
si
->
getFalseValue
(),
dl
,
symbols
);
...
...
@@ -589,11 +593,11 @@ Box* interpretFunction(llvm::Function *f, int nargs, Box* arg1, Box* arg2, Box*
else
SET
(
vf
);
continue
;
}
else
if
(
llvm
::
PHINode
*
phi
=
llvm
::
dyn_cast
<
llvm
::
PHINode
>
(
it
))
{
}
else
if
(
llvm
::
PHINode
*
phi
=
llvm
::
dyn_cast
<
llvm
::
PHINode
>
(
i
ns
t
))
{
assert
(
prevblock
);
SET
(
fetch
(
phi
->
getIncomingValueForBlock
(
prevblock
),
dl
,
symbols
));
continue
;
}
else
if
(
llvm
::
BranchInst
*
br
=
llvm
::
dyn_cast
<
llvm
::
BranchInst
>
(
it
))
{
}
else
if
(
llvm
::
BranchInst
*
br
=
llvm
::
dyn_cast
<
llvm
::
BranchInst
>
(
i
ns
t
))
{
prevblock
=
curblock
;
if
(
br
->
isConditional
())
{
Val
t
=
fetch
(
br
->
getCondition
(),
dl
,
symbols
);
...
...
@@ -609,7 +613,7 @@ Box* interpretFunction(llvm::Function *f, int nargs, Box* arg1, Box* arg2, Box*
//printf("jumped to %s\n", curblock->getName().data());
//}
break
;
}
else
if
(
llvm
::
ReturnInst
*
ret
=
llvm
::
dyn_cast
<
llvm
::
ReturnInst
>
(
it
))
{
}
else
if
(
llvm
::
ReturnInst
*
ret
=
llvm
::
dyn_cast
<
llvm
::
ReturnInst
>
(
i
ns
t
))
{
llvm
::
Value
*
r
=
ret
->
getReturnValue
();
#ifdef TIME_INTERPRETS
...
...
@@ -625,7 +629,7 @@ Box* interpretFunction(llvm::Function *f, int nargs, Box* arg1, Box* arg2, Box*
}
it
->
dump
();
i
ns
t
->
dump
();
RELEASE_ASSERT
(
1
,
""
);
}
}
...
...
src/codegen/opt/dead_allocs.cpp
View file @
ac004e74
...
...
@@ -144,6 +144,7 @@ class DeadAllocsPass : public FunctionPass {
if
(
GetElementPtrInst
*
gep
=
dyn_cast
<
GetElementPtrInst
>
(
derived
))
{
std
::
vector
<
Value
*>
indices
;
// No range version of this for now:
for
(
GetElementPtrInst
::
op_iterator
it
=
gep
->
idx_begin
(),
end
=
gep
->
idx_end
();
it
!=
end
;
++
it
)
{
indices
.
push_back
(
it
->
get
());
}
...
...
@@ -291,6 +292,7 @@ class DeadAllocsPass : public FunctionPass {
// Extract a Value corresponding to the value of this pointer, potentially traversing the CFG.
// Starts looking a the end of this BB and working backwards.
Value
*
getLoadValFrom
(
Value
*
ptr
,
BasicBlock
*
bb
,
std
::
unordered_map
<
BasicBlock
*
,
Value
*>
&
seen
,
ChainInfo
&
chain
)
{
// No range version of this for now:
for
(
auto
it
=
bb
->
rbegin
(),
end
=
bb
->
rend
();
it
!=
end
;
++
it
)
{
Value
*
v
=
extractLoadValue
(
ptr
,
&*
it
,
chain
);
if
(
v
==
NULL
)
...
...
@@ -318,6 +320,7 @@ class DeadAllocsPass : public FunctionPass {
if
(
VERBOSITY
()
>=
2
)
errs
()
<<
"Added phi "
<<
*
phi
<<
" in "
<<
bb
->
getName
()
<<
'\n'
;
int
num_predecessors
=
0
;
// No range version of this for now:
for
(
auto
prev_bb
=
pred_begin
(
bb
),
end
=
pred_end
(
bb
);
prev_bb
!=
end
;
++
prev_bb
)
{
num_predecessors
++
;
...
...
src/codegen/opt/inliner.cpp
View file @
ac004e74
...
...
@@ -175,18 +175,19 @@ class MyInliningPass : public llvm::FunctionPass {
// Keep this section as a release_assert since the code-to-be-inlined, as well as the inlining
// decisions, can be different in release mode:
int
op_idx
=
0
;
for
(
llvm
::
Function
::
arg_iterator
it
=
f
->
arg_begin
(),
end
=
f
->
arg_end
();
it
!=
end
;
++
it
,
++
op_idx
)
{
int
op_idx
=
-
1
;
for
(
llvm
::
Argument
&
arg
:
f
->
args
())
{
++
op_idx
;
llvm
::
Type
*
op_type
=
call
->
getOperand
(
op_idx
)
->
getType
();
if
(
it
->
getType
()
!=
op_type
)
{
if
(
arg
.
getType
()
!=
op_type
)
{
llvm
::
errs
()
<<
f
->
getName
()
<<
" has arg "
<<
op_idx
<<
" mismatched!
\n
"
;
llvm
::
errs
()
<<
"Given "
;
op_type
->
dump
();
llvm
::
errs
()
<<
" but underlying function expected "
;
it
->
getType
()
->
dump
();
arg
.
getType
()
->
dump
();
llvm
::
errs
()
<<
'\n'
;
}
RELEASE_ASSERT
(
it
->
getType
()
==
call
->
getOperand
(
op_idx
)
->
getType
(),
""
);
RELEASE_ASSERT
(
arg
.
getType
()
==
call
->
getOperand
(
op_idx
)
->
getType
(),
""
);
}
assert
(
!
f
->
isDeclaration
());
...
...
src/codegen/patchpoints.cpp
View file @
ac004e74
...
...
@@ -104,9 +104,8 @@ void processStackmap(StackMap* stackmap) {
registerCompiledPatchpoint
(
start_addr
,
pp
,
StackInfo
({
stack_size
,
has_scratch
,
pp
->
numScratchBytes
(),
scratch_rbp_offset
}),
std
::
move
(
live_outs
));
}
for
(
std
::
unordered_map
<
int64_t
,
PatchpointSetupInfo
*>::
iterator
it
=
new_patchpoints_by_id
.
begin
(),
end
=
new_patchpoints_by_id
.
end
();
it
!=
end
;
++
it
)
{
delete
it
->
second
;
for
(
const
std
::
pair
<
int64_t
,
PatchpointSetupInfo
*>
&
p
:
new_patchpoints_by_id
)
{
delete
p
.
second
;
}
new_patchpoints_by_id
.
clear
();
}
...
...
src/core/stats.cpp
View file @
ac004e74
...
...
@@ -45,8 +45,8 @@ void Stats::dump() {
printf
(
"Stats:
\n
"
);
std
::
vector
<
std
::
pair
<
std
::
string
,
int
>
>
pairs
;
for
(
std
::
unordered_map
<
int
,
std
::
string
>::
iterator
it
=
names
->
begin
(),
end
=
names
->
end
();
it
!=
end
;
++
it
)
{
pairs
.
push_back
(
make_pair
(
it
->
second
,
it
->
first
));
for
(
auto
p
:
*
names
)
{
pairs
.
push_back
(
make_pair
(
p
.
second
,
p
.
first
));
}
std
::
sort
(
pairs
.
begin
(),
pairs
.
end
());
...
...
src/runtime/dict.cpp
View file @
ac004e74
...
...
@@ -27,15 +27,15 @@ Box* dictRepr(BoxedDict* self) {
std
::
vector
<
char
>
chars
;
chars
.
push_back
(
'{'
);
bool
first
=
true
;
for
(
BoxedDict
::
PyDict
::
iterator
it
=
self
->
d
.
begin
(),
end
=
self
->
d
.
end
();
it
!=
end
;
++
it
)
{
for
(
auto
p
:
self
->
d
)
{
if
(
!
first
)
{
chars
.
push_back
(
','
);
chars
.
push_back
(
' '
);
}
first
=
false
;
BoxedString
*
k
=
repr
(
it
->
first
);
BoxedString
*
v
=
repr
(
it
->
second
);
BoxedString
*
k
=
repr
(
p
.
first
);
BoxedString
*
v
=
repr
(
p
.
second
);
chars
.
insert
(
chars
.
end
(),
k
->
s
.
begin
(),
k
->
s
.
end
());
chars
.
push_back
(
':'
);
chars
.
push_back
(
' '
);
...
...
@@ -48,10 +48,10 @@ Box* dictRepr(BoxedDict* self) {
Box
*
dictItems
(
BoxedDict
*
self
)
{
BoxedList
*
rtn
=
new
BoxedList
();
for
(
BoxedDict
::
PyDict
::
const_iterator
it
=
self
->
d
.
begin
(),
end
=
self
->
d
.
end
();
it
!=
end
;
++
it
)
{
for
(
auto
p
:
self
->
d
)
{
std
::
vector
<
Box
*>
elts
;
elts
.
push_back
(
it
->
first
);
elts
.
push_back
(
it
->
second
);
elts
.
push_back
(
p
.
first
);
elts
.
push_back
(
p
.
second
);
BoxedTuple
*
t
=
new
BoxedTuple
(
elts
);
listAppendInternal
(
rtn
,
t
);
}
...
...
@@ -61,16 +61,16 @@ Box* dictItems(BoxedDict* self) {
Box
*
dictValues
(
BoxedDict
*
self
)
{
BoxedList
*
rtn
=
new
BoxedList
();
for
(
BoxedDict
::
PyDict
::
const_iterator
it
=
self
->
d
.
begin
(),
end
=
self
->
d
.
end
();
it
!=
end
;
++
it
)
{
listAppendInternal
(
rtn
,
it
->
second
);
for
(
auto
p
:
self
->
d
)
{
listAppendInternal
(
rtn
,
p
.
second
);
}
return
rtn
;
}
Box
*
dictKeys
(
BoxedDict
*
self
)
{
BoxedList
*
rtn
=
new
BoxedList
();
for
(
BoxedDict
::
PyDict
::
const_iterator
it
=
self
->
d
.
begin
(),
end
=
self
->
d
.
end
();
it
!=
end
;
++
it
)
{
listAppendInternal
(
rtn
,
it
->
first
);
for
(
auto
p
:
self
->
d
)
{
listAppendInternal
(
rtn
,
p
.
first
);
}
return
rtn
;
}
...
...
src/runtime/inline/gc_runtime.cpp
View file @
ac004e74
...
...
@@ -92,8 +92,8 @@ void gc_teardown() {
#ifdef DEBUG_GC
AliveSet *alive = getAlive();
assert(nallocs == alive->size());
for (
AliveSet::iterator it = alive->begin(), end = alive->end(); it != end; ++it
) {
printf("%p\n",
*it
);
for (
void* p : alive
) {
printf("%p\n",
p
);
}
#endif
// This will scan through the heap and alert us about things that
...
...
src/runtime/objmodel.cpp
View file @
ac004e74
...
...
@@ -419,9 +419,8 @@ void HCBox::setattr(const std::string& attr, Box* val, SetattrRewriteArgs *rewri
// TODO need to make sure we don't need to rearrange the attributes
assert
(
new_hcls
->
attr_offsets
[
attr
]
==
numattrs
);
#ifndef NDEBUG
for
(
std
::
unordered_map
<
std
::
string
,
int
>::
iterator
it
=
hcls
->
attr_offsets
.
begin
(),
end
=
hcls
->
attr_offsets
.
end
();
it
!=
end
;
++
it
)
{
assert
(
new_hcls
->
attr_offsets
[
it
->
first
]
==
it
->
second
);
for
(
auto
p
:
hcls
->
attr_offsets
)
{
assert
(
new_hcls
->
attr_offsets
[
p
.
first
]
==
p
.
second
);
}
#endif
...
...
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