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
7fb7aaa5
Commit
7fb7aaa5
authored
Mar 03, 2015
by
Michael Arntzenius
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
with statements now handle exceptions properly
parent
be19d931
Changes
18
Show whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
638 additions
and
546 deletions
+638
-546
src/codegen/compvars.cpp
src/codegen/compvars.cpp
+2
-0
src/codegen/compvars.h
src/codegen/compvars.h
+1
-1
src/codegen/irgen.cpp
src/codegen/irgen.cpp
+112
-39
src/codegen/irgen/irgenerator.cpp
src/codegen/irgen/irgenerator.cpp
+27
-4
src/codegen/irgen/irgenerator.h
src/codegen/irgen/irgenerator.h
+5
-1
src/core/ast.h
src/core/ast.h
+3
-3
src/core/cfg.cpp
src/core/cfg.cpp
+465
-487
src/core/cfg.h
src/core/cfg.h
+4
-0
src/core/common.h
src/core/common.h
+2
-0
src/core/util.h
src/core/util.h
+4
-2
src/runtime/types.cpp
src/runtime/types.cpp
+3
-0
test/tests/exceptions_test.py
test/tests/exceptions_test.py
+0
-2
test/tests/sys_test.py
test/tests/sys_test.py
+1
-0
test/tests/try_class.py
test/tests/try_class.py
+1
-0
test/tests/try_def.py
test/tests/try_def.py
+1
-0
test/tests/with_class_redefine.py
test/tests/with_class_redefine.py
+1
-1
test/tests/with_ctxclass_order_of_access.py
test/tests/with_ctxclass_order_of_access.py
+2
-2
test/tests/with_functiondef.py
test/tests/with_functiondef.py
+4
-4
No files found.
src/codegen/compvars.cpp
View file @
7fb7aaa5
...
...
@@ -738,6 +738,8 @@ CompilerVariable* makeFunction(IREmitter& emitter, CLFunction* f, CompilerVariab
llvm
::
Value
*
isGenerator_v
=
llvm
::
ConstantInt
::
get
(
g
.
i1
,
isGenerator
,
false
);
// We know this function call can't throw, so it's safe to use emitter.getBuilder()->CreateCall() rather than
// emitter.createCall().
llvm
::
Value
*
boxed
=
emitter
.
getBuilder
()
->
CreateCall
(
g
.
funcs
.
boxCLFunction
,
std
::
vector
<
llvm
::
Value
*>
{
embedConstantPtr
(
f
,
g
.
llvm_clfunction_type_ptr
),
closure_v
,
isGenerator_v
,
scratch
,
...
...
src/codegen/compvars.h
View file @
7fb7aaa5
...
...
@@ -286,7 +286,7 @@ private:
protected:
void
drop
(
IREmitter
&
emitter
)
override
{
type
->
drop
(
emitter
,
this
);
}
void
grab
(
IREmitter
&
em
mitter
)
override
{
type
->
grab
(
em
mitter
,
this
);
}
void
grab
(
IREmitter
&
em
itter
)
override
{
type
->
grab
(
e
mitter
,
this
);
}
public:
ValuedCompilerVariable
(
T
*
type
,
V
value
,
bool
grabbed
)
:
CompilerVariable
(
grabbed
),
type
(
type
),
value
(
value
)
{
...
...
src/codegen/irgen.cpp
View file @
7fb7aaa5
...
...
@@ -358,8 +358,8 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
llvm_entry_blocks
[
block
]
=
llvm
::
BasicBlock
::
Create
(
g
.
context
,
buf
,
irstate
->
getLLVMFunction
());
}
llvm
::
BasicBlock
*
osr_entry_block
=
NULL
;
// the function entry block, where we add the type guards [no guards anymore]
// the function entry block, where we add the type guards [no guards anymore]
llvm
::
BasicBlock
*
osr_entry_block
=
NULL
;
llvm
::
BasicBlock
*
osr_unbox_block_end
=
NULL
;
// the block after type guards where we up/down-convert things
ConcreteSymbolTable
*
osr_syms
=
NULL
;
// syms after conversion
if
(
entry_descriptor
!=
NULL
)
{
...
...
@@ -516,6 +516,8 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
created_phis
[
block
]
=
phis
;
// Set initial symbol table:
// If we're in the starting block, no phis or symbol table changes for us.
// Generate function entry code instead.
if
(
block
==
source
->
cfg
->
getStartingBlock
())
{
assert
(
entry_descriptor
==
NULL
);
...
...
@@ -650,32 +652,94 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
ASSERT
(
phi_ending_symbol_tables
[
pred
]
->
size
()
==
0
,
"%d %d"
,
block
->
idx
,
pred
->
idx
);
assert
(
ending_symbol_tables
.
count
(
pred
));
// Filter out any names set by an invoke statement at the end
// of the previous block, if we're in the unwind path.
// This definitely doesn't seem like the most elegant way to do this,
// but the rest of the analysis frameworks can't (yet) support the idea of
// a block flowing differently to its different predecessors.
// Filter out any names set by an invoke statement at the end of the previous block, if we're in the
// unwind path. This definitely doesn't seem like the most elegant way to do this, but the rest of the
// analysis frameworks can't (yet) support the idea of a block flowing differently to its different
// successors.
//
// AST statements which can set a name:
// - Assign
// - ClassDef
// - FunctionDef
// - Import, ImportFrom: these get translated into Assigns by our CFG pass.
//
// We only need to do this in the case that we have exactly one predecessor, because:
// - a block ending in an invoke will have multiple successors
// - critical edges (block with multiple successors -> block with multiple predecessors)
// are disallowed
// FIXME: this doesn't work if we're over-writing a local name that's already been defined.
// see with_class_redefine.py
//
// There isn't any great solution to this except to fundamentally change the way we handle this problem.
//
// One solution:
// - make "create-class" & "create-function" expressions, and translate classdefs into:
//
// Assign(temporary, CreateClass(....))
// Assign(classname, temporary)
//
// Then the Assign(temporary, CreateClass(...)) will go in an invoke, which is fine because we never
// reuse temporary names.
// FIXME: Unfortunately, doing this will create heisenbugs unless the name we're removing is a
// temporary.
//
// The reason being, our definedness analysis passes don't know *anything* about how invoke statements
// don't actually define anything on the exception pass. Which means that their information and our
// symbol tables will disagree. This manifests in various ways, such as `sameKeyset()` asserts tripping.
// The only good solutions to this are:
//
// 1. making our analysis passes understand invokes
// 2. making assignments inside invokes always be to temporary variables
// (and never referring to those temporary variables on the exception path)
//
// TODO(rntz): implement #2. This would also solve the above FIXME in this file.
auto
pred
=
block
->
predecessors
[
0
];
auto
last_inst
=
pred
->
body
.
back
();
SymbolTable
*
sym_table
=
ending_symbol_tables
[
pred
];
bool
created_new_sym_table
=
false
;
if
(
last_inst
->
type
==
AST_TYPE
::
Invoke
)
{
auto
invoke
=
ast_cast
<
AST_Invoke
>
(
last_inst
);
if
(
invoke
->
exc_dest
==
block
&&
invoke
->
stmt
->
type
==
AST_TYPE
::
Assign
)
{
auto
asgn
=
ast_cast
<
AST_Assign
>
(
invoke
->
stmt
);
if
(
last_inst
->
type
==
AST_TYPE
::
Invoke
&&
ast_cast
<
AST_Invoke
>
(
last_inst
)
->
exc_dest
==
block
)
{
AST_stmt
*
stmt
=
ast_cast
<
AST_Invoke
>
(
last_inst
)
->
stmt
;
bool
remove_name
=
false
;
InternedString
name
;
if
(
stmt
->
type
==
AST_TYPE
::
Assign
)
{
auto
asgn
=
ast_cast
<
AST_Assign
>
(
stmt
);
assert
(
asgn
->
targets
.
size
()
==
1
);
if
(
asgn
->
targets
[
0
]
->
type
==
AST_TYPE
::
Name
)
{
auto
name
=
ast_cast
<
AST_Name
>
(
asgn
->
targets
[
0
]);
// TODO: inneficient
name
=
ast_cast
<
AST_Name
>
(
asgn
->
targets
[
0
])
->
id
;
remove_name
=
true
;
// You might think I need to check whether `name' is being assigned globally or locally,
// since a global assign doesn't affect the symbol table. However, the CFG pass only
// generates invoke-assigns to temporary variables. Just to be sure, we assert:
assert
(
!
source
->
getScopeInfo
()
->
refersToGlobal
(
name
));
}
}
else
if
(
stmt
->
type
==
AST_TYPE
::
ClassDef
)
{
// However, here we *do* have to check for global scope. :(
name
=
ast_cast
<
AST_ClassDef
>
(
stmt
)
->
name
;
remove_name
=
!
source
->
getScopeInfo
()
->
refersToGlobal
(
name
);
}
else
if
(
stmt
->
type
==
AST_TYPE
::
FunctionDef
)
{
// and here, as well.
name
=
ast_cast
<
AST_FunctionDef
>
(
stmt
)
->
name
;
remove_name
=
!
source
->
getScopeInfo
()
->
refersToGlobal
(
name
);
}
// The CFG pass translates away these statements, so we should never encounter them. If we did, we
// would need to remove a name here.
assert
(
stmt
->
type
!=
AST_TYPE
::
Import
);
assert
(
stmt
->
type
!=
AST_TYPE
::
ImportFrom
);
if
(
remove_name
)
{
// TODO: inefficient
sym_table
=
new
SymbolTable
(
*
sym_table
);
ASSERT
(
sym_table
->
count
(
name
->
id
),
"%d %s
\n
"
,
block
->
idx
,
name
->
id
.
c_str
());
sym_table
->
erase
(
name
->
id
);
// FIXME: is getting tripped sometimes by classdef
ASSERT
(
sym_table
->
count
(
name
),
"%d %s
\n
"
,
block
->
idx
,
name
.
c_str
());
sym_table
->
erase
(
name
);
created_new_sym_table
=
true
;
}
}
}
generator
->
copySymbolsFrom
(
sym_table
);
if
(
created_new_sym_table
)
...
...
@@ -689,21 +753,25 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
// Start off with the non-phi ones:
generator
->
copySymbolsFrom
(
ending_symbol_tables
[
pred
]);
// NB. This is where most `typical' phi nodes get added.
// And go through and add phi nodes:
ConcreteSymbolTable
*
pred_st
=
phi_ending_symbol_tables
[
pred
];
for
(
ConcreteSymbolTable
::
iterator
it
=
pred_st
->
begin
();
it
!=
pred_st
->
end
();
++
it
)
{
// printf("adding phi for %s\n", it->first.c_str());
llvm
::
PHINode
*
phi
=
emitter
->
getBuilder
()
->
CreatePHI
(
it
->
second
->
getType
()
->
llvmType
(),
block
->
predecessors
.
size
(),
it
->
first
.
str
());
for
(
auto
it
=
pred_st
->
begin
();
it
!=
pred_st
->
end
();
++
it
)
{
InternedString
name
=
it
->
first
;
ConcreteCompilerVariable
*
cv
=
it
->
second
;
// incoming CCV from predecessor block
// printf("block %d: adding phi for %s from pred %d\n", block->idx, name.c_str(), pred->idx);
llvm
::
PHINode
*
phi
=
emitter
->
getBuilder
()
->
CreatePHI
(
cv
->
getType
()
->
llvmType
(),
block
->
predecessors
.
size
(),
name
.
str
());
// emitter->getBuilder()->CreateCall(g.funcs.dump, phi);
ConcreteCompilerVariable
*
var
=
new
ConcreteCompilerVariable
(
it
->
second
->
getType
(),
phi
,
true
);
generator
->
giveLocalSymbol
(
it
->
first
,
var
);
ConcreteCompilerVariable
*
var
=
new
ConcreteCompilerVariable
(
cv
->
getType
(),
phi
,
true
);
generator
->
giveLocalSymbol
(
name
,
var
);
(
*
phis
)[
it
->
first
]
=
std
::
make_pair
(
it
->
second
->
getType
(),
phi
);
}
}
}
// Generate loop safepoints on backedges.
for
(
CFGBlock
*
predecessor
:
block
->
predecessors
)
{
if
(
predecessor
->
idx
>
block
->
idx
)
{
// Loop safepoint:
...
...
@@ -713,6 +781,7 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
}
}
// Generate the IR for the block.
generator
->
run
(
block
);
const
IRGenerator
::
EndingState
&
ending_st
=
generator
->
getEndingSymbolTable
();
...
...
@@ -725,7 +794,7 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
}
////
// Phi
gener
ation.
// Phi
popul
ation.
// We don't know the exact ssa values to back-propagate to the phi nodes until we've generated
// the relevant IR, so after we have done all of it, go back through and populate the phi nodes.
// Also, do some checking to make sure that the phi analysis stuff worked out, and that all blocks
...
...
@@ -737,19 +806,23 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
bool
this_is_osr_entry
=
(
entry_descriptor
&&
b
==
entry_descriptor
->
backedge
->
target
);
#ifndef NDEBUG
// Check to see that all blocks agree on what symbols + types they should be propagating for phis.
for
(
int
j
=
0
;
j
<
b
->
predecessors
.
size
();
j
++
)
{
CFGBlock
*
b
2
=
b
->
predecessors
[
j
];
if
(
blocks
.
count
(
b
2
)
==
0
)
CFGBlock
*
b
pred
=
b
->
predecessors
[
j
];
if
(
blocks
.
count
(
b
pred
)
==
0
)
continue
;
// printf("(%d %ld) -> (%d %ld)\n", b2->idx, phi_ending_symbol_tables[b2]->size(), b->idx, phis->size());
compareKeyset
(
phi_ending_symbol_tables
[
b2
],
phis
);
assert
(
phi_ending_symbol_tables
[
b2
]
->
size
()
==
phis
->
size
());
// printf("(%d %ld) -> (%d %ld)\n", bpred->idx, phi_ending_symbol_tables[bpred]->size(), b->idx,
// phis->size());
assert
(
sameKeyset
(
phi_ending_symbol_tables
[
bpred
],
phis
));
assert
(
phi_ending_symbol_tables
[
bpred
]
->
size
()
==
phis
->
size
());
}
if
(
this_is_osr_entry
)
{
compareKeyset
(
osr_syms
,
phis
);
assert
(
sameKeyset
(
osr_syms
,
phis
)
);
}
#endif // end checking phi agreement.
// Can't always add the phi incoming value right away, since we may have to create more
// basic blocks as part of type coercion.
...
...
@@ -757,21 +830,22 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
// which we won't read until after all new BBs have been added.
std
::
vector
<
std
::
tuple
<
llvm
::
PHINode
*
,
llvm
::
Value
*
,
llvm
::
BasicBlock
*&>>
phi_args
;
for
(
PHITable
::
iterator
it
=
phis
->
begin
();
it
!=
phis
->
end
();
++
it
)
{
for
(
auto
it
=
phis
->
begin
();
it
!=
phis
->
end
();
++
it
)
{
llvm
::
PHINode
*
llvm_phi
=
it
->
second
.
second
;
for
(
int
j
=
0
;
j
<
b
->
predecessors
.
size
();
j
++
)
{
CFGBlock
*
b
2
=
b
->
predecessors
[
j
];
if
(
blocks
.
count
(
b
2
)
==
0
)
CFGBlock
*
b
pred
=
b
->
predecessors
[
j
];
if
(
blocks
.
count
(
b
pred
)
==
0
)
continue
;
ConcreteCompilerVariable
*
v
=
(
*
phi_ending_symbol_tables
[
b
2
])[
it
->
first
];
ConcreteCompilerVariable
*
v
=
(
*
phi_ending_symbol_tables
[
b
pred
])[
it
->
first
];
assert
(
v
);
assert
(
v
->
isGrabbed
());
// Make sure they all prepared for the same type:
ASSERT
(
it
->
second
.
first
==
v
->
getType
(),
"%d %d: %s %s %s"
,
b
->
idx
,
b
2
->
idx
,
it
->
first
.
c_str
(),
ASSERT
(
it
->
second
.
first
==
v
->
getType
(),
"%d %d: %s %s %s"
,
b
->
idx
,
b
pred
->
idx
,
it
->
first
.
c_str
(),
it
->
second
.
first
->
debugName
().
c_str
(),
v
->
getType
()
->
debugName
().
c_str
());
llvm
::
Value
*
val
=
v
->
getValue
();
llvm_phi
->
addIncoming
(
v
->
getValue
(),
llvm_exit_blocks
[
b
->
predecessors
[
j
]]);
}
...
...
@@ -783,14 +857,13 @@ static void emitBBs(IRGenState* irstate, TypeAnalysis* types, const OSREntryDesc
ASSERT
(
it
->
second
.
first
==
v
->
getType
(),
""
);
llvm_phi
->
addIncoming
(
v
->
getValue
(),
osr_unbox_block_end
);
}
InternedString
is_defined_name
=
getIsDefinedName
(
it
->
first
,
source
->
getInternedStrings
());
}
for
(
auto
t
:
phi_args
)
{
std
::
get
<
0
>
(
t
)
->
addIncoming
(
std
::
get
<
1
>
(
t
),
std
::
get
<
2
>
(
t
));
}
}
// deallocate/dereference memory
for
(
CFGBlock
*
b
:
source
->
cfg
->
blocks
)
{
if
(
ending_symbol_tables
[
b
]
==
NULL
)
continue
;
...
...
src/codegen/irgen/irgenerator.cpp
View file @
7fb7aaa5
...
...
@@ -291,6 +291,7 @@ private:
llvm
::
BasicBlock
*
curblock
;
IREmitterImpl
emitter
;
// symbol_table tracks which (non-global) python variables are bound to which CompilerVariables
SymbolTable
symbol_table
;
std
::
unordered_map
<
CFGBlock
*
,
llvm
::
BasicBlock
*>&
entry_blocks
;
CFGBlock
*
myblock
;
...
...
@@ -883,6 +884,7 @@ private:
return
closure
->
getattr
(
emitter
,
getEmptyOpInfo
(
unw_info
),
&
node
->
id
.
str
(),
false
);
}
else
{
// vst is one of {FAST, CLOSURE, NAME}
if
(
symbol_table
.
find
(
node
->
id
)
==
symbol_table
.
end
())
{
// classdefs have different scoping rules than functions:
if
(
vst
==
ScopeInfo
::
VarScopeType
::
NAME
)
{
...
...
@@ -1225,6 +1227,12 @@ private:
cur
=
val
;
}
// whether a Python variable FOO might be undefined or not is determined by whether the corresponding is_defined_FOO
// variable is present in our symbol table. If it is, then it *might* be undefined. If it isn't, then it either is
// definitely defined, or definitely isn't.
//
// to check whether a variable is in our symbol table, call _getFake with allow_missing = true and check whether the
// result is NULL.
CompilerVariable
*
_getFake
(
InternedString
name
,
bool
allow_missing
=
false
)
{
assert
(
name
.
str
()[
0
]
==
'!'
);
auto
it
=
symbol_table
.
find
(
name
);
...
...
@@ -1243,6 +1251,7 @@ private:
return
rtn
;
}
// only updates symbol_table if we're *not* setting a global
void
_doSet
(
InternedString
name
,
CompilerVariable
*
val
,
UnwindInfo
unw_info
)
{
assert
(
name
.
str
()
!=
"None"
);
...
...
@@ -1702,7 +1711,7 @@ private:
// that case asking it to convert to itself ends up just being an incvref
// and doesn't end up emitting an incref+decref pair.
// This could also be handled by casting from the CompilerVariable to
// ConcreteC
O
mpilerVariable, but this way feels a little more robust to me.
// ConcreteC
o
mpilerVariable, but this way feels a little more robust to me.
ConcreteCompilerType
*
opt_rtn_type
=
irstate
->
getReturnType
();
if
(
irstate
->
getReturnType
()
->
llvmType
()
==
val
->
getConcreteType
()
->
llvmType
())
opt_rtn_type
=
val
->
getConcreteType
();
...
...
@@ -2060,9 +2069,9 @@ private:
SourceInfo
*
source
=
irstate
->
getSourceInfo
();
ScopeInfo
*
scope_info
=
irstate
->
getScopeInfo
();
// Additional names to remove; remove them after iteration is done to n
ew
mess up the iterators
// Additional names to remove; remove them after iteration is done to n
ot
mess up the iterators
std
::
vector
<
InternedString
>
also_remove
;
for
(
SymbolTable
::
iterator
it
=
symbol_table
.
begin
();
it
!=
symbol_table
.
end
();)
{
for
(
auto
it
=
symbol_table
.
begin
();
it
!=
symbol_table
.
end
();)
{
if
(
allowableFakeEndingSymbol
(
it
->
first
))
{
++
it
;
continue
;
...
...
@@ -2208,6 +2217,8 @@ public:
return
EndingState
(
st
,
phi_st
,
curblock
);
}
// We have one successor, but they have more than one predecessor.
// We're going to sort out which symbols need to go in phi_st and which belong inst.
for
(
SymbolTable
::
iterator
it
=
st
->
begin
();
it
!=
st
->
end
();)
{
if
(
allowableFakeEndingSymbol
(
it
->
first
)
||
source
->
phis
->
isRequiredAfter
(
it
->
first
,
myblock
))
{
ASSERT
(
it
->
second
->
isGrabbed
(),
"%s"
,
it
->
first
.
c_str
());
...
...
@@ -2344,12 +2355,24 @@ public:
}
void
run
(
const
CFGBlock
*
block
)
override
{
if
(
VERBOSITY
(
"irgenerator"
)
>=
1
)
{
// print starting symbol table
printf
(
" %d init:"
,
block
->
idx
);
for
(
auto
it
=
symbol_table
.
begin
();
it
!=
symbol_table
.
end
();
++
it
)
printf
(
" %s"
,
it
->
first
.
c_str
());
printf
(
"
\n
"
);
}
for
(
int
i
=
0
;
i
<
block
->
body
.
size
();
i
++
)
{
if
(
state
==
DEAD
)
break
;
assert
(
state
!=
FINISHED
);
doStmt
(
block
->
body
[
i
],
UnwindInfo
(
block
->
body
[
i
],
NULL
));
}
if
(
VERBOSITY
(
"irgenerator"
)
>=
1
)
{
// print ending symbol table
printf
(
" %d fini:"
,
block
->
idx
);
for
(
auto
it
=
symbol_table
.
begin
();
it
!=
symbol_table
.
end
();
++
it
)
printf
(
" %s"
,
it
->
first
.
c_str
());
printf
(
"
\n
"
);
}
}
void
doSafePoint
()
override
{
emitter
.
getBuilder
()
->
CreateCall
(
g
.
funcs
.
allowGLReadPreemption
);
}
...
...
src/codegen/irgen/irgenerator.h
View file @
7fb7aaa5
...
...
@@ -95,10 +95,14 @@ public:
ParamNames
*
getParamNames
()
{
return
param_names
;
}
};
// turns CFGBlocks into LLVM IR
class
IRGenerator
{
private:
public:
struct
EndingState
{
// symbol_table records which Python variables are bound to what CompilerVariables at the end of this block.
// phi_symbol_table records the ones that will need to be `phi'd.
// both only record non-globals.
SymbolTable
*
symbol_table
;
ConcreteSymbolTable
*
phi_symbol_table
;
llvm
::
BasicBlock
*
ending_block
;
...
...
@@ -113,7 +117,7 @@ public:
virtual
void
giveLocalSymbol
(
InternedString
name
,
CompilerVariable
*
var
)
=
0
;
virtual
void
copySymbolsFrom
(
SymbolTable
*
st
)
=
0
;
virtual
void
run
(
const
CFGBlock
*
block
)
=
0
;
virtual
void
run
(
const
CFGBlock
*
block
)
=
0
;
// primary entry point
virtual
EndingState
getEndingSymbolTable
()
=
0
;
virtual
void
doSafePoint
()
=
0
;
virtual
void
addFrameStackmapArgs
(
PatchpointInfo
*
pp
,
AST_stmt
*
current_stmt
,
...
...
src/core/ast.h
View file @
7fb7aaa5
...
...
@@ -118,7 +118,7 @@ enum AST_TYPE {
DictComp
=
15
,
Set
=
43
,
Ellipsis
=
87
,
Expression
=
88
,
Expression
=
88
,
// like Module, but used for eval.
// Pseudo-nodes that are specific to this compiler:
Branch
=
200
,
...
...
@@ -1007,14 +1007,14 @@ class AST_LangPrimitive : public AST_expr {
public:
enum
Opcodes
{
ISINSTANCE
,
LANDINGPAD
,
LANDINGPAD
,
// grabs the info about the last raised exception
LOCALS
,
GET_ITER
,
IMPORT_FROM
,
IMPORT_NAME
,
IMPORT_STAR
,
NONE
,
NONZERO
,
NONZERO
,
// determines whether something is "true" for purposes of `if'
SET_EXC_INFO
,
UNCACHE_EXC_INFO
,
}
opcode
;
...
...
src/core/cfg.cpp
View file @
7fb7aaa5
...
...
@@ -36,7 +36,8 @@ void CFGBlock::connectTo(CFGBlock* successor, bool allow_backedge) {
if
(
!
allow_backedge
)
{
assert
(
this
->
idx
>=
0
);
ASSERT
(
successor
->
idx
==
-
1
||
successor
->
idx
>
this
->
idx
,
"edge from %d to %d"
,
this
->
idx
,
successor
->
idx
);
ASSERT
(
successor
->
idx
==
-
1
||
successor
->
idx
>
this
->
idx
,
"edge from %d (%s) to %d (%s)"
,
this
->
idx
,
this
->
info
,
successor
->
idx
,
successor
->
info
);
}
// assert(successors.count(successor) == 0);
// assert(successor->predecessors.count(this) == 0);
...
...
@@ -53,16 +54,123 @@ void CFGBlock::unconnectFrom(CFGBlock* successor) {
successor
->
predecessors
.
end
());
}
void
CFGBlock
::
print
()
{
printf
(
"Block %d"
,
idx
);
if
(
info
)
printf
(
" '%s'"
,
info
);
printf
(
"; Predecessors:"
);
for
(
int
j
=
0
;
j
<
predecessors
.
size
();
j
++
)
{
printf
(
" %d"
,
predecessors
[
j
]
->
idx
);
}
printf
(
" Successors:"
);
for
(
int
j
=
0
;
j
<
successors
.
size
();
j
++
)
{
printf
(
" %d"
,
successors
[
j
]
->
idx
);
}
printf
(
"
\n
"
);
PrintVisitor
pv
(
4
);
for
(
int
j
=
0
;
j
<
body
.
size
();
j
++
)
{
printf
(
" "
);
body
[
j
]
->
accept
(
&
pv
);
printf
(
"
\n
"
);
}
}
static
const
std
::
string
RETURN_NAME
(
"#rtnval"
);
// The various reasons why a `finally' block (or similar, eg. a `with' exit block) might get entered.
// this has to go outside CFGVisitor b/c why_values can't go inside it.
enum
Why
:
int8_t
{
FALLTHROUGH
,
// i.e. normal control flow
CONTINUE
,
BREAK
,
RETURN
,
EXCEPTION
,
};
static
const
Why
why_values
[]
=
{
FALLTHROUGH
,
CONTINUE
,
BREAK
,
RETURN
,
EXCEPTION
};
class
CFGVisitor
:
public
ASTVisitor
{
// ---------- Types ----------
private:
/* Explanation of ContInfo and ExcBlockInfo:
*
* While generating the CFG, we need to know what to do if we:
* 1. hit a `continue'
* 2. hit a `break'
* 3. hit a `return'
* 4. raise an exception
*
* We call these "continuations", because they're what we "continue on to" after these conditions occur.
*
* Various control flow constructs affect each of these:
* - `for' and `while' affect (1-2).
* - `try/except' affects (4).
* - `try/finally' and `with' affect all four.
*
* Each of these take effect only within some chunk of code. So, notionally, we keep a stack for each of (1-4) whose
* _top_ value says what to do if that condition occurs. The top of the continue-stack points to the block to jump
* to if we hit a `continue', etc.
*
* For example, when we enter a loop, we push a pointer to the head of the loop onto the continue-stack, and a
* pointer to the code after the loop onto the break-stack. When we visit a `break' in the loop body, we emit a jump
* to the top of the break-stack, which is the end of the loop. After we finish visiting the loop body, we pop the
* break- & continue-stacks, restoring our old state (maybe we were inside another loop, for example).
*
* It's more complicated in practice, because:
*
* 1. When we jump to a `finally' block, we must tell it *why* we jumped to it. After the `finally' block finishes,
* it uses this info to resume what we were doing before we entered it (returning, raising an exception, etc).
*
* 2. When we jump to a `except' block, we must record three pieces of information about the exception (its type,
* value, and traceback).
*
* So instead of four stacks of block pointers, instead we have two stacks:
* - `continuations', a stack of ContInfos, for `continue', `break', and `return'
* - `exc_handlers', a stack of ExcBlockInfos, for exceptions
*
* Read the comments in ContInfo & ExcBlockInfo for more information.
*/
struct
ContInfo
{
// where to jump to if a continue, break, or return happens respectively
CFGBlock
*
continue_dest
,
*
break_dest
,
*
return_dest
;
// true if this continuation needs to know the reason why we entered it. `finally' blocks use this info to
// determine how to resume execution after they finish.
bool
say_why
;
// bit-vector tracking all reasons Why we ever might enter this continuation. is only updated/used if `say_why'
// is true. when we emit a jump to this continuation for reason w, we set the bit (did_why & (1 << w)). this is
// used when emitting `finally' blocks to determine which continuation-cases to emit.
int
did_why
;
// name of the variable to store the reason Why we jumped in.
InternedString
why_name
;
ContInfo
(
CFGBlock
*
continue_dest
,
CFGBlock
*
break_dest
,
CFGBlock
*
return_dest
,
bool
say_why
,
InternedString
why_name
)
:
continue_dest
(
continue_dest
),
break_dest
(
break_dest
),
return_dest
(
return_dest
),
say_why
(
say_why
),
did_why
(
0
),
why_name
(
why_name
)
{}
};
struct
ExcBlockInfo
{
// where to jump in case of an exception
CFGBlock
*
exc_dest
;
// variable names to store the exception (type, value, traceback) in
InternedString
exc_type_name
,
exc_value_name
,
exc_traceback_name
;
};
// ---------- Member fields ----------
private:
SourceInfo
*
source
;
// `root_type' is the type of the root of the AST tree that we are turning
// into a CFG. Used when we find a "return" to check that we're inside a
// function (otherwise we SyntaxError).
AST_TYPE
::
AST_TYPE
root_type
;
FutureFlags
future_flags
;
CFG
*
cfg
;
CFGBlock
*
curblock
;
ScopingAnalysis
*
scoping_analysis
;
std
::
vector
<
ContInfo
>
continuations
;
std
::
vector
<
ExcBlockInfo
>
exc_handlers
;
public:
CFGVisitor
(
SourceInfo
*
source
,
AST_TYPE
::
AST_TYPE
root_type
,
FutureFlags
future_flags
,
...
...
@@ -74,32 +182,12 @@ public:
}
~
CFGVisitor
()
{
assert
(
reg
ions
.
size
()
==
0
);
assert
(
continuat
ions
.
size
()
==
0
);
assert
(
exc_handlers
.
size
()
==
0
);
}
// ---------- private methods ----------
private:
enum
Why
:
int8_t
{
FALLTHROUGH
,
CONTINUE
,
BREAK
,
RETURN
,
EXCEPTION
,
};
// My first thought is to call this BlockInfo, but this is separate from the idea of cfg blocks.
struct
RegionInfo
{
CFGBlock
*
continue_dest
,
*
break_dest
,
*
return_dest
;
bool
say_why
;
int
did_why
;
InternedString
why_name
;
RegionInfo
(
CFGBlock
*
continue_dest
,
CFGBlock
*
break_dest
,
CFGBlock
*
return_dest
,
bool
say_why
,
InternedString
why_name
)
:
continue_dest
(
continue_dest
),
break_dest
(
break_dest
),
return_dest
(
return_dest
),
say_why
(
say_why
),
did_why
(
0
),
why_name
(
why_name
)
{}
};
template
<
typename
T
>
InternedString
internString
(
T
&&
s
)
{
return
source
->
getInternedStrings
().
get
(
std
::
forward
<
T
>
(
s
));
}
...
...
@@ -109,48 +197,32 @@ private:
return
name
;
}
std
::
vector
<
RegionInfo
>
regions
;
AST_Name
*
makeLoad
(
InternedString
id
,
AST
*
node
)
{
return
makeName
(
id
,
AST_TYPE
::
Load
,
node
->
lineno
);
}
struct
ExcBlockInfo
{
CFGBlock
*
exc_dest
;
InternedString
exc_type_name
,
exc_value_name
,
exc_traceback_name
;
};
std
::
vector
<
ExcBlockInfo
>
exc_handlers
;
void
pushLoopRegion
(
CFGBlock
*
continue_dest
,
CFGBlock
*
break_dest
)
{
void
pushLoopContinuation
(
CFGBlock
*
continue_dest
,
CFGBlock
*
break_dest
)
{
assert
(
continue_dest
!=
break_dest
);
// I guess this doesn't have to be true, but validates passing say_why=false
reg
ions
.
emplace_back
(
continue_dest
,
break_dest
,
nullptr
,
false
,
internString
(
""
));
continuat
ions
.
emplace_back
(
continue_dest
,
break_dest
,
nullptr
,
false
,
internString
(
""
));
}
void
pushFinally
Reg
ion
(
CFGBlock
*
finally_block
,
InternedString
why_name
)
{
reg
ions
.
emplace_back
(
finally_block
,
finally_block
,
finally_block
,
true
,
why_name
);
void
pushFinally
Continuat
ion
(
CFGBlock
*
finally_block
,
InternedString
why_name
)
{
continuat
ions
.
emplace_back
(
finally_block
,
finally_block
,
finally_block
,
true
,
why_name
);
}
void
popRegion
()
{
regions
.
pop_back
();
}
// XXX get rid of this
void
pushReturnRegion
(
CFGBlock
*
return_dest
)
{
regions
.
emplace_back
(
nullptr
,
nullptr
,
return_dest
,
false
,
internString
(
""
));
}
void
popContinuation
()
{
continuations
.
pop_back
();
}
void
doReturn
(
AST_expr
*
value
)
{
assert
(
value
);
for
(
auto
&
region
:
llvm
::
make_range
(
regions
.
rbegin
(),
reg
ions
.
rend
()))
{
if
(
region
.
return_dest
)
{
if
(
region
.
say_why
)
{
pushAssign
(
region
.
why_name
,
makeNum
(
Why
::
RETURN
));
region
.
did_why
|=
(
1
<<
Why
::
RETURN
);
for
(
auto
&
cont
:
llvm
::
make_range
(
continuations
.
rbegin
(),
continuat
ions
.
rend
()))
{
if
(
cont
.
return_dest
)
{
if
(
cont
.
say_why
)
{
pushAssign
(
cont
.
why_name
,
makeNum
(
Why
::
RETURN
));
cont
.
did_why
|=
(
1
<<
Why
::
RETURN
);
}
pushAssign
(
internString
(
RETURN_NAME
),
value
);
AST_Jump
*
j
=
makeJump
();
j
->
target
=
region
.
return_dest
;
curblock
->
connectTo
(
region
.
return_dest
);
push_back
(
j
);
curblock
=
NULL
;
pushJump
(
cont
.
return_dest
);
return
;
}
}
...
...
@@ -164,18 +236,14 @@ private:
}
void
doContinue
()
{
for
(
auto
&
region
:
llvm
::
make_range
(
regions
.
rbegin
(),
reg
ions
.
rend
()))
{
if
(
region
.
continue_dest
)
{
if
(
region
.
say_why
)
{
pushAssign
(
region
.
why_name
,
makeNum
(
Why
::
CONTINUE
));
region
.
did_why
|=
(
1
<<
Why
::
CONTINUE
);
for
(
auto
&
cont
:
llvm
::
make_range
(
continuations
.
rbegin
(),
continuat
ions
.
rend
()))
{
if
(
cont
.
continue_dest
)
{
if
(
cont
.
say_why
)
{
pushAssign
(
cont
.
why_name
,
makeNum
(
Why
::
CONTINUE
));
cont
.
did_why
|=
(
1
<<
Why
::
CONTINUE
);
}
AST_Jump
*
j
=
makeJump
();
j
->
target
=
region
.
continue_dest
;
curblock
->
connectTo
(
region
.
continue_dest
,
true
);
push_back
(
j
);
curblock
=
NULL
;
pushJump
(
cont
.
continue_dest
,
true
);
return
;
}
}
...
...
@@ -184,18 +252,14 @@ private:
}
void
doBreak
()
{
for
(
auto
&
region
:
llvm
::
make_range
(
regions
.
rbegin
(),
reg
ions
.
rend
()))
{
if
(
region
.
break_dest
)
{
if
(
region
.
say_why
)
{
pushAssign
(
region
.
why_name
,
makeNum
(
Why
::
BREAK
));
region
.
did_why
|=
(
1
<<
Why
::
BREAK
);
for
(
auto
&
cont
:
llvm
::
make_range
(
continuations
.
rbegin
(),
continuat
ions
.
rend
()))
{
if
(
cont
.
break_dest
)
{
if
(
cont
.
say_why
)
{
pushAssign
(
cont
.
why_name
,
makeNum
(
Why
::
BREAK
));
cont
.
did_why
|=
(
1
<<
Why
::
BREAK
);
}
AST_Jump
*
j
=
makeJump
();
j
->
target
=
region
.
break_dest
;
curblock
->
connectTo
(
region
.
break_dest
,
true
);
push_back
(
j
);
curblock
=
NULL
;
pushJump
(
cont
.
break_dest
,
true
);
return
;
}
}
...
...
@@ -216,7 +280,7 @@ private:
auto
name
=
nodeName
(
e
);
pushAssign
(
name
,
call
);
return
make
Name
(
name
,
AST_TYPE
::
Load
,
e
->
lineno
);
return
make
Load
(
name
,
e
);
}
AST_Name
*
remapName
(
AST_Name
*
name
)
{
return
name
;
}
...
...
@@ -254,21 +318,13 @@ private:
pushAssign
(
iter_name
,
iter_call
);
// TODO bad to save these like this?
AST_expr
*
hasnext_attr
=
makeLoadAttribute
(
makeName
(
iter_name
,
AST_TYPE
::
Load
,
node
->
lineno
),
internString
(
"__hasnext__"
),
true
);
AST_expr
*
next_attr
=
makeLoadAttribute
(
makeName
(
iter_name
,
AST_TYPE
::
Load
,
node
->
lineno
),
internString
(
"next"
),
true
);
AST_Jump
*
j
;
AST_expr
*
hasnext_attr
=
makeLoadAttribute
(
makeLoad
(
iter_name
,
node
),
internString
(
"__hasnext__"
),
true
);
AST_expr
*
next_attr
=
makeLoadAttribute
(
makeLoad
(
iter_name
,
node
),
internString
(
"next"
),
true
);
CFGBlock
*
test_block
=
cfg
->
addBlock
();
test_block
->
info
=
"comprehension_test"
;
// printf("Test block for comp %d is %d\n", i, test_block->idx);
j
=
new
AST_Jump
();
j
->
target
=
test_block
;
curblock
->
connectTo
(
test_block
);
push_back
(
j
);
pushJump
(
test_block
);
curblock
=
test_block
;
AST_expr
*
test_call
=
callNonzero
(
remapExpr
(
makeCall
(
hasnext_attr
)));
...
...
@@ -293,7 +349,7 @@ private:
curblock
=
body_block
;
InternedString
next_name
(
nodeName
(
next_attr
));
pushAssign
(
next_name
,
makeCall
(
next_attr
));
pushAssign
(
c
->
target
,
make
Name
(
next_name
,
AST_TYPE
::
Load
,
node
->
lineno
));
pushAssign
(
c
->
target
,
make
Load
(
next_name
,
node
));
for
(
AST_expr
*
if_condition
:
c
->
ifs
)
{
AST_expr
*
remapped
=
callNonzero
(
remapExpr
(
if_condition
));
...
...
@@ -315,10 +371,7 @@ private:
curblock
->
connectTo
(
body_continue
);
curblock
=
body_tramp
;
j
=
new
AST_Jump
();
j
->
target
=
test_block
;
push_back
(
j
);
curblock
->
connectTo
(
test_block
,
true
);
pushJump
(
test_block
,
true
);
curblock
=
body_continue
;
}
...
...
@@ -328,21 +381,15 @@ private:
assert
((
finished_block
!=
NULL
)
==
(
i
!=
0
));
if
(
finished_block
)
{
curblock
=
exit_block
;
j
=
new
AST_Jump
();
j
->
target
=
finished_block
;
curblock
->
connectTo
(
finished_block
,
true
);
push_back
(
j
);
pushJump
(
finished_block
,
true
);
}
finished_block
=
test_block
;
curblock
=
body_end
;
if
(
is_innermost
)
{
push_back
(
makeExpr
(
applyComprehensionCall
(
node
,
make
Name
(
rtn_name
,
AST_TYPE
::
Load
,
node
->
lineno
))));
push_back
(
makeExpr
(
applyComprehensionCall
(
node
,
make
Load
(
rtn_name
,
node
))));
j
=
new
AST_Jump
();
j
->
target
=
test_block
;
curblock
->
connectTo
(
test_block
,
true
);
push_back
(
j
);
pushJump
(
test_block
,
true
);
assert
(
exit_blocks
.
size
());
curblock
=
exit_blocks
[
0
];
...
...
@@ -359,7 +406,7 @@ private:
// printf("Exit block for comp %d is %d\n", i, exit_blocks[i]->idx);
}
return
make
Name
(
rtn_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
return
make
Load
(
rtn_name
,
node
);
}
AST_expr
*
makeNum
(
int
n
)
{
...
...
@@ -369,11 +416,15 @@ private:
return
node
;
}
AST_Jump
*
makeJump
(
)
{
void
pushJump
(
CFGBlock
*
target
,
bool
allow_backedge
=
false
)
{
AST_Jump
*
rtn
=
new
AST_Jump
();
return
rtn
;
rtn
->
target
=
target
;
push_back
(
rtn
);
curblock
->
connectTo
(
target
,
allow_backedge
);
curblock
=
nullptr
;
}
// NB. can generate blocks, because callNonzero can
AST_Branch
*
makeBranch
(
AST_expr
*
test
)
{
AST_Branch
*
rtn
=
new
AST_Branch
();
rtn
->
test
=
callNonzero
(
test
);
...
...
@@ -382,6 +433,30 @@ private:
return
rtn
;
}
// NB. this can (but usually doesn't) generate new blocks, which is why we require `iftrue' and `iffalse' to be
// deferred, to avoid heisenbugs. of course, this doesn't allow these branches to be backedges, but that hasn't yet
// been necessary.
void
pushBranch
(
AST_expr
*
test
,
CFGBlock
*
iftrue
,
CFGBlock
*
iffalse
)
{
assert
(
iftrue
->
idx
==
-
1
&&
iffalse
->
idx
==
-
1
);
AST_Branch
*
branch
=
makeBranch
(
test
);
branch
->
iftrue
=
iftrue
;
branch
->
iffalse
=
iffalse
;
curblock
->
connectTo
(
iftrue
);
curblock
->
connectTo
(
iffalse
);
push_back
(
branch
);
curblock
=
nullptr
;
}
void
pushReraise
(
AST
*
node
,
InternedString
exc_type_name
,
InternedString
exc_value_name
,
InternedString
exc_traceback_name
)
{
auto
raise
=
new
AST_Raise
();
raise
->
arg0
=
makeLoad
(
exc_type_name
,
node
);
raise
->
arg1
=
makeLoad
(
exc_value_name
,
node
);
raise
->
arg2
=
makeLoad
(
exc_traceback_name
,
node
);
push_back
(
raise
);
curblock
=
nullptr
;
}
AST_expr
*
makeLoadAttribute
(
AST_expr
*
base
,
InternedString
name
,
bool
clsonly
)
{
AST_expr
*
rtn
;
if
(
clsonly
)
{
...
...
@@ -412,28 +487,34 @@ private:
}
AST_Call
*
makeCall
(
AST_expr
*
func
,
AST_expr
*
arg0
)
{
AST_Call
*
call
=
new
AST_Call
(
);
auto
call
=
makeCall
(
func
);
call
->
args
.
push_back
(
arg0
);
call
->
starargs
=
NULL
;
call
->
kwargs
=
NULL
;
call
->
func
=
func
;
call
->
col_offset
=
func
->
col_offset
;
call
->
lineno
=
func
->
lineno
;
return
call
;
}
AST_Call
*
makeCall
(
AST_expr
*
func
,
AST_expr
*
arg0
,
AST_expr
*
arg1
)
{
AST_Call
*
call
=
new
AST_Call
(
);
auto
call
=
makeCall
(
func
);
call
->
args
.
push_back
(
arg0
);
call
->
args
.
push_back
(
arg1
);
call
->
starargs
=
NULL
;
call
->
kwargs
=
NULL
;
call
->
func
=
func
;
call
->
col_offset
=
func
->
col_offset
;
call
->
lineno
=
func
->
lineno
;
return
call
;
}
AST_Call
*
makeCall
(
AST_expr
*
func
,
AST_expr
*
arg0
,
AST_expr
*
arg1
,
AST_expr
*
arg2
)
{
auto
call
=
makeCall
(
func
);
call
->
args
.
push_back
(
arg0
);
call
->
args
.
push_back
(
arg1
);
call
->
args
.
push_back
(
arg2
);
return
call
;
}
AST_Compare
*
makeCompare
(
AST_TYPE
::
AST_TYPE
oper
,
AST_expr
*
left
,
AST_expr
*
right
)
{
auto
compare
=
new
AST_Compare
();
compare
->
ops
.
push_back
(
AST_TYPE
::
Eq
);
compare
->
left
=
left
;
compare
->
comparators
.
push_back
(
right
);
return
compare
;
}
void
pushAssign
(
AST_expr
*
target
,
AST_expr
*
val
)
{
AST_Assign
*
assign
=
new
AST_Assign
();
assign
->
value
=
val
;
...
...
@@ -495,7 +576,7 @@ private:
InternedString
tmp_name
=
nodeName
(
target
,
""
,
i
);
new_target
->
elts
.
push_back
(
makeName
(
tmp_name
,
AST_TYPE
::
Store
,
target
->
lineno
));
pushAssign
((
*
elts
)[
i
],
make
Name
(
tmp_name
,
AST_TYPE
::
Load
,
target
->
lineno
));
pushAssign
((
*
elts
)[
i
],
make
Load
(
tmp_name
,
target
));
}
}
else
{
RELEASE_ASSERT
(
0
,
"%d"
,
target
->
type
);
...
...
@@ -516,11 +597,10 @@ private:
return
stmt
;
}
InternedString
nodeName
(
AST
*
node
)
{
char
buf
[
40
];
snprintf
(
buf
,
40
,
"#%p"
,
node
);
int
bytes
=
snprintf
(
buf
,
40
,
"#%p"
,
node
);
assert
(
bytes
<
40
);
// double-check
// Uncomment this line to check to make sure we never reuse the same nodeName() accidentally.
// This check is potentially too expensive for even debug mode, since it never frees any memory.
// #define VALIDATE_FAKE_NAMES
...
...
@@ -537,13 +617,15 @@ private:
InternedString
nodeName
(
AST
*
node
,
const
std
::
string
&
suffix
)
{
char
buf
[
50
];
snprintf
(
buf
,
50
,
"#%p_%s"
,
node
,
suffix
.
c_str
());
int
bytes
=
snprintf
(
buf
,
50
,
"#%p_%s"
,
node
,
suffix
.
c_str
());
assert
(
bytes
<
50
);
// double-check
return
internString
(
std
::
string
(
buf
));
}
InternedString
nodeName
(
AST
*
node
,
const
std
::
string
&
suffix
,
int
idx
)
{
char
buf
[
50
];
snprintf
(
buf
,
50
,
"#%p_%s_%d"
,
node
,
suffix
.
c_str
(),
idx
);
int
bytes
=
snprintf
(
buf
,
50
,
"#%p_%s_%d"
,
node
,
suffix
.
c_str
(),
idx
);
assert
(
bytes
<
50
);
// double-check
return
internString
(
std
::
string
(
buf
));
}
...
...
@@ -639,26 +721,19 @@ private:
}
curblock
=
crit_break_block
;
AST_Jump
*
j
=
new
AST_Jump
();
j
->
target
=
exit_block
;
push_back
(
j
);
crit_break_block
->
connectTo
(
exit_block
);
pushJump
(
exit_block
);
curblock
=
next_block
;
}
AST_expr
*
final_val
=
remapExpr
(
node
->
values
[
node
->
values
.
size
()
-
1
]);
pushAssign
(
name
,
final_val
);
AST_Jump
*
j
=
new
AST_Jump
();
push_back
(
j
);
j
->
target
=
exit_block
;
curblock
->
connectTo
(
exit_block
);
pushJump
(
exit_block
);
cfg
->
placeBlock
(
exit_block
);
curblock
=
exit_block
;
return
make
Name
(
name
,
AST_TYPE
::
Load
,
node
->
lineno
);
return
make
Load
(
name
,
node
);
}
AST_expr
*
remapCall
(
AST_Call
*
node
)
{
...
...
@@ -738,7 +813,7 @@ private:
pushAssign
(
name
,
val
);
AST_Branch
*
br
=
new
AST_Branch
();
br
->
test
=
callNonzero
(
make
Name
(
name
,
AST_TYPE
::
Load
,
node
->
lineno
));
br
->
test
=
callNonzero
(
make
Load
(
name
,
node
));
push_back
(
br
);
CFGBlock
*
was_block
=
curblock
;
...
...
@@ -751,25 +826,18 @@ private:
br
->
iftrue
=
next_block
;
curblock
=
crit_break_block
;
AST_Jump
*
j
=
new
AST_Jump
();
j
->
target
=
exit_block
;
push_back
(
j
);
crit_break_block
->
connectTo
(
exit_block
);
pushJump
(
exit_block
);
curblock
=
next_block
;
left
=
_dup
(
right
);
}
AST_Jump
*
j
=
new
AST_Jump
();
push_back
(
j
);
j
->
target
=
exit_block
;
curblock
->
connectTo
(
exit_block
);
pushJump
(
exit_block
);
cfg
->
placeBlock
(
exit_block
);
curblock
=
exit_block
;
return
make
Name
(
name
,
AST_TYPE
::
Load
,
node
->
lineno
);
return
make
Load
(
name
,
node
);
}
}
...
...
@@ -816,7 +884,7 @@ private:
loop
->
target
=
c
->
target
;
if
(
i
==
0
)
{
loop
->
iter
=
make
Name
(
first_generator_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
loop
->
iter
=
make
Load
(
first_generator_name
,
node
);
}
else
{
loop
->
iter
=
c
->
iter
;
}
...
...
@@ -847,50 +915,38 @@ private:
call
->
starargs
=
NULL
;
call
->
kwargs
=
NULL
;
call
->
func
=
make
Name
(
func_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
call
->
func
=
make
Load
(
func_name
,
node
);
call
->
args
.
push_back
(
first
);
return
call
;
};
AST_expr
*
remapIfExp
(
AST_IfExp
*
node
)
{
InternedString
rtn_name
=
nodeName
(
node
);
CFGBlock
*
iftrue
=
cfg
->
addDeferredBlock
();
CFGBlock
*
iffalse
=
cfg
->
addDeferredBlock
();
CFGBlock
*
exit_block
=
cfg
->
addDeferredBlock
();
AST_Branch
*
br
=
new
AST_Branch
();
br
->
col_offset
=
node
->
col_offset
;
br
->
lineno
=
node
->
lineno
;
br
->
test
=
callNonzero
(
remapExpr
(
node
->
test
));
push_back
(
br
);
CFGBlock
*
starting_block
=
curblock
;
pushBranch
(
remapExpr
(
node
->
test
),
iftrue
,
iffalse
);
CFGBlock
*
iftrue
=
cfg
->
addBlock
();
iftrue
->
info
=
"iftrue"
;
br
->
iftrue
=
iftrue
;
starting_block
->
connectTo
(
iftrue
);
// if true block
cfg
->
placeBlock
(
iftrue
);
curblock
=
iftrue
;
iftrue
->
info
=
"iftrue"
;
pushAssign
(
rtn_name
,
remapExpr
(
node
->
body
));
AST_Jump
*
jtrue
=
new
AST_Jump
();
push_back
(
jtrue
);
CFGBlock
*
endtrue
=
curblock
;
pushJump
(
exit_block
);
CFGBlock
*
iffalse
=
cfg
->
addBlock
();
iffalse
->
info
=
"iffalse"
;
br
->
iffalse
=
iffalse
;
starting_block
->
connectTo
(
iffalse
);
// if false block
cfg
->
placeBlock
(
iffalse
);
curblock
=
iffalse
;
iffalse
->
info
=
"iffalse"
;
pushAssign
(
rtn_name
,
remapExpr
(
node
->
orelse
));
AST_Jump
*
jfalse
=
new
AST_Jump
();
push_back
(
jfalse
);
CFGBlock
*
endfalse
=
curblock
;
CFGBlock
*
exit_block
=
cfg
->
addBlock
();
jtrue
->
target
=
exit_block
;
endtrue
->
connectTo
(
exit_block
);
jfalse
->
target
=
exit_block
;
endfalse
->
connectTo
(
exit_block
);
pushJump
(
exit_block
);
// exit block
cfg
->
placeBlock
(
exit_block
);
curblock
=
exit_block
;
return
make
Name
(
rtn_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
return
make
Load
(
rtn_name
,
node
);
}
AST_expr
*
remapIndex
(
AST_Index
*
node
)
{
...
...
@@ -1012,9 +1068,14 @@ private:
push_back
(
makeExpr
(
new
AST_LangPrimitive
(
AST_LangPrimitive
::
UNCACHE_EXC_INFO
)));
return
make
Name
(
node_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
return
make
Load
(
node_name
,
node
);
}
// Flattens a nested expression into a flat one, emitting instructions &
// generating temporary variables as needed.
//
// If `wrap_with_assign` is true, it will always return a temporary
// variable.
AST_expr
*
remapExpr
(
AST_expr
*
node
,
bool
wrap_with_assign
=
true
)
{
if
(
node
==
NULL
)
return
NULL
;
...
...
@@ -1100,15 +1161,65 @@ private:
RELEASE_ASSERT
(
0
,
"%d"
,
node
->
type
);
}
// this is the part that actually generates temporaries & assigns to them.
if
(
wrap_with_assign
&&
(
rtn
->
type
!=
AST_TYPE
::
Name
||
ast_cast
<
AST_Name
>
(
rtn
)
->
id
.
str
()[
0
]
!=
'#'
))
{
InternedString
name
=
nodeName
(
node
);
pushAssign
(
name
,
rtn
);
return
make
Name
(
name
,
AST_TYPE
::
Load
,
node
->
lineno
);
return
make
Load
(
name
,
node
);
}
else
{
return
rtn
;
}
}
// helper for visit_{tryfinally,with}
CFGBlock
*
makeFinallyCont
(
Why
reason
,
AST_expr
*
whyexpr
,
CFGBlock
*
then_block
)
{
CFGBlock
*
otherwise
=
cfg
->
addDeferredBlock
();
otherwise
->
info
=
"finally_otherwise"
;
pushBranch
(
makeCompare
(
AST_TYPE
::
Eq
,
whyexpr
,
makeNum
(
reason
)),
then_block
,
otherwise
);
cfg
->
placeBlock
(
otherwise
);
return
otherwise
;
}
// Helper for visit_with. Performs the appropriate exit from a with-block, according to the value of `why'.
// NB. `exit_block' is only used if `why' is FALLTHROUGH.
void
exitFinally
(
AST
*
node
,
Why
why
,
CFGBlock
*
exit_block
=
nullptr
)
{
switch
(
why
)
{
case
Why
:
:
RETURN
:
doReturn
(
makeLoad
(
internString
(
RETURN_NAME
),
node
));
break
;
case
Why
:
:
BREAK
:
doBreak
();
break
;
case
Why
:
:
CONTINUE
:
doContinue
();
break
;
case
Why
:
:
FALLTHROUGH
:
assert
(
exit_block
);
pushJump
(
exit_block
);
break
;
case
Why
:
:
EXCEPTION
:
assert
(
why
!=
Why
::
EXCEPTION
);
// not handled here
break
;
}
assert
(
curblock
==
nullptr
);
}
// helper for visit_{with,tryfinally}. Generates a branch testing the value of `whyexpr' against `why', and
// performing the appropriate exit from the with-block if they are equal.
// NB. `exit_block' is only used if `why' is FALLTHROUGH.
void
exitFinallyIf
(
AST
*
node
,
Why
why
,
InternedString
whyname
,
CFGBlock
*
exit_block
=
nullptr
)
{
CFGBlock
*
do_exit
=
cfg
->
addDeferredBlock
();
do_exit
->
info
=
"with_exit_if"
;
CFGBlock
*
otherwise
=
makeFinallyCont
(
why
,
makeLoad
(
whyname
,
node
),
do_exit
);
cfg
->
placeBlock
(
do_exit
);
curblock
=
do_exit
;
exitFinally
(
node
,
why
,
exit_block
);
curblock
=
otherwise
;
}
// ---------- public methods ----------
public:
void
push_back
(
AST_stmt
*
node
)
{
assert
(
node
->
type
!=
AST_TYPE
::
Invoke
);
...
...
@@ -1145,6 +1256,7 @@ public:
if
(
asgn
->
targets
[
0
]
->
type
==
AST_TYPE
::
Name
)
{
AST_Name
*
target
=
ast_cast
<
AST_Name
>
(
asgn
->
targets
[
0
]);
if
(
target
->
id
.
str
()[
0
]
!=
'#'
)
{
// assigning to a non-temporary
#ifndef NDEBUG
if
(
!
(
asgn
->
value
->
type
==
AST_TYPE
::
Name
&&
ast_cast
<
AST_Name
>
(
asgn
->
value
)
->
id
.
str
()[
0
]
==
'#'
)
&&
asgn
->
value
->
type
!=
AST_TYPE
::
Str
&&
asgn
->
value
->
type
!=
AST_TYPE
::
Num
)
{
...
...
@@ -1160,8 +1272,12 @@ public:
// Assigning from one temporary name to another:
curblock
->
push_back
(
node
);
return
;
}
else
if
(
asgn
->
value
->
type
==
AST_TYPE
::
Num
||
asgn
->
value
->
type
==
AST_TYPE
::
Str
)
{
}
else
if
(
asgn
->
value
->
type
==
AST_TYPE
::
Num
||
asgn
->
value
->
type
==
AST_TYPE
::
Str
||
(
asgn
->
value
->
type
==
AST_TYPE
::
Name
&&
ast_cast
<
AST_Name
>
(
asgn
->
value
)
->
id
.
str
().
compare
(
"None"
)
==
0
))
{
// Assigning to a temporary name from an expression that can't throw:
// NB. `None' can't throw in Python, because it's hardcoded
// (seriously, try reassigning "None" in CPython).
curblock
->
push_back
(
node
);
return
;
}
...
...
@@ -1206,10 +1322,7 @@ public:
exc_asgn
->
value
=
new
AST_LangPrimitive
(
AST_LangPrimitive
::
LANDINGPAD
);
curblock
->
push_back
(
exc_asgn
);
AST_Jump
*
j
=
new
AST_Jump
();
j
->
target
=
exc_info
.
exc_dest
;
curblock
->
push_back
(
j
);
curblock
->
connectTo
(
exc_info
.
exc_dest
);
pushJump
(
exc_info
.
exc_dest
);
if
(
is_raise
)
curblock
=
NULL
;
...
...
@@ -1289,7 +1402,7 @@ public:
if
(
a
->
asname
.
str
().
size
()
==
0
)
{
// No asname, so load the top-level module into the name
// (e.g., for `import os.path`, loads the os module into `os`)
pushAssign
(
internString
(
getTopModule
(
a
->
name
.
str
())),
make
Name
(
tmpname
,
AST_TYPE
::
Load
,
node
->
lineno
));
pushAssign
(
internString
(
getTopModule
(
a
->
name
.
str
())),
make
Load
(
tmpname
,
node
));
}
else
{
// If there is an asname, get the bottom-level module by
// getting the attributes and load it into asname.
...
...
@@ -1303,11 +1416,11 @@ public:
l
=
r
+
1
;
continue
;
}
pushAssign
(
tmpname
,
new
AST_Attribute
(
make
Name
(
tmpname
,
AST_TYPE
::
Load
,
node
->
lineno
)
,
AST_TYPE
::
Load
,
internString
(
a
->
name
.
str
().
substr
(
l
,
r
))));
pushAssign
(
tmpname
,
new
AST_Attribute
(
make
Load
(
tmpname
,
node
),
AST_TYPE
::
Load
,
internString
(
a
->
name
.
str
().
substr
(
l
,
r
))));
l
=
r
+
1
;
}
while
(
l
<
a
->
name
.
str
().
size
());
pushAssign
(
a
->
asname
,
make
Name
(
tmpname
,
AST_TYPE
::
Load
,
node
->
lineno
));
pushAssign
(
a
->
asname
,
make
Load
(
tmpname
,
node
));
}
}
...
...
@@ -1348,7 +1461,7 @@ public:
AST_LangPrimitive
*
import_star
=
new
AST_LangPrimitive
(
AST_LangPrimitive
::
IMPORT_STAR
);
import_star
->
lineno
=
node
->
lineno
;
import_star
->
col_offset
=
node
->
col_offset
;
import_star
->
args
.
push_back
(
make
Name
(
tmp_module_name
,
AST_TYPE
::
Load
,
node
->
lineno
));
import_star
->
args
.
push_back
(
make
Load
(
tmp_module_name
,
node
));
AST_Expr
*
import_star_expr
=
new
AST_Expr
();
import_star_expr
->
value
=
import_star
;
...
...
@@ -1358,13 +1471,12 @@ public:
AST_LangPrimitive
*
import_from
=
new
AST_LangPrimitive
(
AST_LangPrimitive
::
IMPORT_FROM
);
import_from
->
lineno
=
node
->
lineno
;
import_from
->
col_offset
=
node
->
col_offset
;
import_from
->
args
.
push_back
(
make
Name
(
tmp_module_name
,
AST_TYPE
::
Load
,
node
->
lineno
));
import_from
->
args
.
push_back
(
make
Load
(
tmp_module_name
,
node
));
import_from
->
args
.
push_back
(
new
AST_Str
(
a
->
name
.
str
()));
InternedString
tmp_import_name
=
nodeName
(
a
);
pushAssign
(
tmp_import_name
,
import_from
);
pushAssign
(
a
->
asname
.
str
().
size
()
?
a
->
asname
:
a
->
name
,
makeName
(
tmp_import_name
,
AST_TYPE
::
Load
,
node
->
lineno
));
pushAssign
(
a
->
asname
.
str
().
size
()
?
a
->
asname
:
a
->
name
,
makeLoad
(
tmp_import_name
,
node
));
}
}
...
...
@@ -1407,17 +1519,10 @@ public:
CFGBlock
*
unreachable
=
cfg
->
addBlock
();
unreachable
->
info
=
"unreachable"
;
curblock
->
connectTo
(
unreachable
);
AST_Jump
*
j
=
new
AST_Jump
();
j
->
target
=
unreachable
;
push_back
(
j
);
pushJump
(
unreachable
);
curblock
=
unreachable
;
AST_Jump
*
j2
=
new
AST_Jump
();
j2
->
target
=
unreachable
;
push_back
(
j2
);
curblock
->
connectTo
(
unreachable
,
true
);
pushJump
(
unreachable
,
true
);
curblock
=
iftrue
;
...
...
@@ -1460,9 +1565,9 @@ public:
AST_Name
*
n
=
ast_cast
<
AST_Name
>
(
node
->
target
);
assert
(
n
->
ctx_type
==
AST_TYPE
::
Store
);
InternedString
n_name
(
nodeName
(
n
));
pushAssign
(
n_name
,
make
Name
(
n
->
id
,
AST_TYPE
::
Load
,
node
->
lineno
));
pushAssign
(
n_name
,
make
Load
(
n
->
id
,
node
));
remapped_target
=
n
;
remapped_lhs
=
make
Name
(
n_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
remapped_lhs
=
make
Load
(
n_name
,
node
);
break
;
}
case
AST_TYPE
:
:
Subscript
:
{
...
...
@@ -1522,7 +1627,7 @@ public:
InternedString
node_name
(
nodeName
(
node
));
pushAssign
(
node_name
,
binop
);
pushAssign
(
remapped_target
,
make
Name
(
node_name
,
AST_TYPE
::
Load
,
node
->
lineno
));
pushAssign
(
remapped_target
,
make
Load
(
node_name
,
node
));
return
true
;
}
...
...
@@ -1637,6 +1742,8 @@ public:
}
bool
visit_return
(
AST_Return
*
node
)
override
{
// returns are allowed in functions (of course), and also in eval("...") strings - basically, eval strings get
// an implicit `return'. root_type is AST_TYPE::Expression when we're compiling an eval string.
if
(
root_type
!=
AST_TYPE
::
FunctionDef
&&
root_type
!=
AST_TYPE
::
Lambda
&&
root_type
!=
AST_TYPE
::
Expression
)
{
raiseExcHelper
(
SyntaxError
,
"'return' outside function"
);
}
...
...
@@ -1644,10 +1751,7 @@ public:
if
(
!
curblock
)
return
true
;
AST_expr
*
value
=
remapExpr
(
node
->
value
);
if
(
value
==
NULL
)
value
=
makeName
(
internString
(
"None"
),
AST_TYPE
::
Load
,
node
->
lineno
);
doReturn
(
value
);
doReturn
(
node
->
value
?
remapExpr
(
node
->
value
)
:
makeLoad
(
internString
(
"None"
),
node
));
return
true
;
}
...
...
@@ -1674,10 +1778,7 @@ public:
node
->
body
[
i
]
->
accept
(
this
);
}
if
(
curblock
)
{
AST_Jump
*
jtrue
=
new
AST_Jump
();
push_back
(
jtrue
);
jtrue
->
target
=
exit
;
curblock
->
connectTo
(
exit
);
pushJump
(
exit
);
}
CFGBlock
*
iffalse
=
cfg
->
addBlock
();
...
...
@@ -1690,10 +1791,7 @@ public:
node
->
orelse
[
i
]
->
accept
(
this
);
}
if
(
curblock
)
{
AST_Jump
*
jfalse
=
new
AST_Jump
();
push_back
(
jfalse
);
jfalse
->
target
=
exit
;
curblock
->
connectTo
(
exit
);
pushJump
(
exit
);
}
if
(
exit
->
predecessors
.
size
()
==
0
)
{
...
...
@@ -1732,11 +1830,7 @@ public:
CFGBlock
*
test_block
=
cfg
->
addBlock
();
test_block
->
info
=
"while_test"
;
AST_Jump
*
j
=
makeJump
();
push_back
(
j
);
j
->
target
=
test_block
;
curblock
->
connectTo
(
test_block
);
pushJump
(
test_block
);
curblock
=
test_block
;
AST_Branch
*
br
=
makeBranch
(
remapExpr
(
node
->
test
));
...
...
@@ -1747,7 +1841,7 @@ public:
// but we don't want it to be placed until after the orelse.
CFGBlock
*
end
=
cfg
->
addDeferredBlock
();
end
->
info
=
"while_exit"
;
pushLoop
Reg
ion
(
test_block
,
end
);
pushLoop
Continuat
ion
(
test_block
,
end
);
CFGBlock
*
body
=
cfg
->
addBlock
();
body
->
info
=
"while_body_start"
;
...
...
@@ -1758,13 +1852,9 @@ public:
for
(
int
i
=
0
;
i
<
node
->
body
.
size
();
i
++
)
{
node
->
body
[
i
]
->
accept
(
this
);
}
if
(
curblock
)
{
AST_Jump
*
jbody
=
makeJump
();
push_back
(
jbody
);
jbody
->
target
=
test_block
;
curblock
->
connectTo
(
test_block
,
true
);
}
popRegion
();
if
(
curblock
)
pushJump
(
test_block
,
true
);
popContinuation
();
CFGBlock
*
orelse
=
cfg
->
addBlock
();
orelse
->
info
=
"while_orelse_start"
;
...
...
@@ -1774,12 +1864,8 @@ public:
for
(
int
i
=
0
;
i
<
node
->
orelse
.
size
();
i
++
)
{
node
->
orelse
[
i
]
->
accept
(
this
);
}
if
(
curblock
)
{
AST_Jump
*
jend
=
makeJump
();
push_back
(
jend
);
jend
->
target
=
end
;
curblock
->
connectTo
(
end
);
}
if
(
curblock
)
pushJump
(
end
);
curblock
=
end
;
cfg
->
placeBlock
(
end
);
...
...
@@ -1804,18 +1890,12 @@ public:
InternedString
itername
=
internString
(
itername_buf
);
pushAssign
(
itername
,
iter_call
);
auto
hasnext_attr
=
[
&
]()
{
return
makeLoadAttribute
(
makeName
(
itername
,
AST_TYPE
::
Load
,
node
->
lineno
),
internString
(
"__hasnext__"
),
true
);
};
AST_expr
*
next_attr
=
makeLoadAttribute
(
makeName
(
itername
,
AST_TYPE
::
Load
,
node
->
lineno
),
internString
(
"next"
),
true
);
auto
hasnext_attr
=
[
&
]()
{
return
makeLoadAttribute
(
makeLoad
(
itername
,
node
),
internString
(
"__hasnext__"
),
true
);
};
AST_expr
*
next_attr
=
makeLoadAttribute
(
makeLoad
(
itername
,
node
),
internString
(
"next"
),
true
);
CFGBlock
*
test_block
=
cfg
->
addBlock
();
AST_Jump
*
jump_to_test
=
makeJump
();
jump_to_test
->
target
=
test_block
;
push_back
(
jump_to_test
);
curblock
->
connectTo
(
test_block
);
pushJump
(
test_block
);
curblock
=
test_block
;
AST_expr
*
test_call
=
makeCall
(
hasnext_attr
());
...
...
@@ -1834,30 +1914,23 @@ public:
CFGBlock
*
else_block
=
cfg
->
addDeferredBlock
();
curblock
=
test_true
;
// TODO simplify the breaking of these crit edges?
AST_Jump
*
test_true_jump
=
makeJump
();
test_true_jump
->
target
=
loop_block
;
push_back
(
test_true_jump
);
test_true
->
connectTo
(
loop_block
);
pushJump
(
loop_block
);
curblock
=
test_false
;
AST_Jump
*
test_false_jump
=
makeJump
();
test_false_jump
->
target
=
else_block
;
push_back
(
test_false_jump
);
test_false
->
connectTo
(
else_block
);
pushJump
(
else_block
);
pushLoop
Reg
ion
(
test_block
,
end_block
);
pushLoop
Continuat
ion
(
test_block
,
end_block
);
curblock
=
loop_block
;
InternedString
next_name
(
nodeName
(
next_attr
));
pushAssign
(
next_name
,
makeCall
(
next_attr
));
pushAssign
(
node
->
target
,
make
Name
(
next_name
,
AST_TYPE
::
Load
,
node
->
lineno
));
pushAssign
(
node
->
target
,
make
Load
(
next_name
,
node
));
for
(
int
i
=
0
;
i
<
node
->
body
.
size
();
i
++
)
{
node
->
body
[
i
]
->
accept
(
this
);
}
pop
Reg
ion
();
pop
Continuat
ion
();
if
(
curblock
)
{
AST_expr
*
end_call
=
makeCall
((
hasnext_attr
()));
...
...
@@ -1872,16 +1945,10 @@ public:
curblock
->
connectTo
(
end_false
);
curblock
=
end_true
;
AST_Jump
*
end_true_jump
=
makeJump
();
end_true_jump
->
target
=
loop_block
;
push_back
(
end_true_jump
);
end_true
->
connectTo
(
loop_block
,
true
);
pushJump
(
loop_block
,
true
);
curblock
=
end_false
;
AST_Jump
*
end_false_jump
=
makeJump
();
end_false_jump
->
target
=
else_block
;
push_back
(
end_false_jump
);
end_false
->
connectTo
(
else_block
);
pushJump
(
else_block
);
}
cfg
->
placeBlock
(
else_block
);
...
...
@@ -1890,12 +1957,8 @@ public:
for
(
int
i
=
0
;
i
<
node
->
orelse
.
size
();
i
++
)
{
node
->
orelse
[
i
]
->
accept
(
this
);
}
if
(
curblock
)
{
AST_Jump
*
else_jump
=
makeJump
();
push_back
(
else_jump
);
else_jump
->
target
=
end_block
;
curblock
->
connectTo
(
end_block
);
}
if
(
curblock
)
pushJump
(
end_block
);
cfg
->
placeBlock
(
end_block
);
curblock
=
end_block
;
...
...
@@ -1956,12 +2019,8 @@ public:
}
CFGBlock
*
join_block
=
cfg
->
addDeferredBlock
();
if
(
curblock
)
{
AST_Jump
*
j
=
new
AST_Jump
();
j
->
target
=
join_block
;
push_back
(
j
);
curblock
->
connectTo
(
join_block
);
}
if
(
curblock
)
pushJump
(
join_block
);
if
(
exc_handler_block
->
predecessors
.
size
()
==
0
)
{
delete
exc_handler_block
;
...
...
@@ -1970,7 +2029,7 @@ public:
curblock
=
exc_handler_block
;
// TODO This is supposed to be exc_type_name (value doesn't matter for checking matches)
AST_expr
*
exc_obj
=
make
Name
(
exc_value_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
AST_expr
*
exc_obj
=
make
Load
(
exc_value_name
,
node
);
bool
caught_all
=
false
;
for
(
AST_ExceptHandler
*
exc_handler
:
node
->
handlers
)
{
...
...
@@ -2003,9 +2062,9 @@ public:
}
AST_LangPrimitive
*
set_exc_info
=
new
AST_LangPrimitive
(
AST_LangPrimitive
::
SET_EXC_INFO
);
set_exc_info
->
args
.
push_back
(
make
Name
(
exc_type_name
,
AST_TYPE
::
Load
,
node
->
lineno
));
set_exc_info
->
args
.
push_back
(
make
Name
(
exc_value_name
,
AST_TYPE
::
Load
,
node
->
lineno
));
set_exc_info
->
args
.
push_back
(
make
Name
(
exc_traceback_name
,
AST_TYPE
::
Load
,
node
->
lineno
));
set_exc_info
->
args
.
push_back
(
make
Load
(
exc_type_name
,
node
));
set_exc_info
->
args
.
push_back
(
make
Load
(
exc_value_name
,
node
));
set_exc_info
->
args
.
push_back
(
make
Load
(
exc_traceback_name
,
node
));
push_back
(
makeExpr
(
set_exc_info
));
if
(
exc_handler
->
name
)
{
...
...
@@ -2017,10 +2076,7 @@ public:
}
if
(
curblock
)
{
AST_Jump
*
j
=
new
AST_Jump
();
j
->
target
=
join_block
;
push_back
(
j
);
curblock
->
connectTo
(
join_block
);
pushJump
(
join_block
);
}
if
(
exc_next
)
{
...
...
@@ -2033,9 +2089,9 @@ public:
if
(
!
caught_all
)
{
AST_Raise
*
raise
=
new
AST_Raise
();
raise
->
arg0
=
make
Name
(
exc_type_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
raise
->
arg1
=
make
Name
(
exc_value_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
raise
->
arg2
=
make
Name
(
exc_traceback_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
raise
->
arg0
=
make
Load
(
exc_type_name
,
node
);
raise
->
arg1
=
make
Load
(
exc_value_name
,
node
);
raise
->
arg2
=
make
Load
(
exc_traceback_name
,
node
);
push_back
(
raise
);
curblock
=
NULL
;
}
...
...
@@ -2061,7 +2117,7 @@ public:
exc_handlers
.
push_back
({
exc_handler_block
,
exc_type_name
,
exc_value_name
,
exc_traceback_name
});
CFGBlock
*
finally_block
=
cfg
->
addDeferredBlock
();
pushFinally
Reg
ion
(
finally_block
,
exc_why_name
);
pushFinally
Continuat
ion
(
finally_block
,
exc_why_name
);
for
(
AST_stmt
*
subnode
:
node
->
body
)
{
subnode
->
accept
(
this
);
...
...
@@ -2069,17 +2125,14 @@ public:
exc_handlers
.
pop_back
();
int
did_why
=
reg
ions
.
back
().
did_why
;
// bad to just reach in like this
pop
Region
();
// finally reg
ion
int
did_why
=
continuat
ions
.
back
().
did_why
;
// bad to just reach in like this
pop
Continuation
();
// finally continuat
ion
if
(
curblock
)
{
// assign the exc_*_name variables to tell irgen that they won't be undefined?
// have an :UNDEF() langprimitive to not have to do any loading there?
pushAssign
(
exc_why_name
,
makeNum
(
Why
::
FALLTHROUGH
));
AST_Jump
*
j
=
new
AST_Jump
();
j
->
target
=
finally_block
;
push_back
(
j
);
curblock
->
connectTo
(
finally_block
);
pushJump
(
finally_block
);
}
if
(
exc_handler_block
->
predecessors
.
size
()
==
0
)
{
...
...
@@ -2087,13 +2140,8 @@ public:
}
else
{
cfg
->
placeBlock
(
exc_handler_block
);
curblock
=
exc_handler_block
;
pushAssign
(
exc_why_name
,
makeNum
(
Why
::
EXCEPTION
));
AST_Jump
*
j
=
new
AST_Jump
();
j
->
target
=
finally_block
;
push_back
(
j
);
curblock
->
connectTo
(
finally_block
);
pushJump
(
finally_block
);
}
cfg
->
placeBlock
(
finally_block
);
...
...
@@ -2104,99 +2152,19 @@ public:
}
if
(
curblock
)
{
// TODO: these 4 cases are pretty copy-pasted from each other:
if
(
did_why
&
(
1
<<
Why
::
RETURN
))
{
CFGBlock
*
doreturn
=
cfg
->
addBlock
();
CFGBlock
*
otherwise
=
cfg
->
addBlock
();
if
(
did_why
&
(
1
<<
Why
::
RETURN
))
exitFinallyIf
(
node
,
Why
::
RETURN
,
exc_why_name
);
if
(
did_why
&
(
1
<<
Why
::
BREAK
))
exitFinallyIf
(
node
,
Why
::
BREAK
,
exc_why_name
);
if
(
did_why
&
(
1
<<
Why
::
CONTINUE
))
exitFinallyIf
(
node
,
Why
::
CONTINUE
,
exc_why_name
);
AST_Compare
*
compare
=
new
AST_Compare
();
compare
->
ops
.
push_back
(
AST_TYPE
::
Eq
);
compare
->
left
=
makeName
(
exc_why_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
compare
->
comparators
.
push_back
(
makeNum
(
Why
::
RETURN
));
AST_Branch
*
br
=
new
AST_Branch
();
br
->
test
=
callNonzero
(
compare
);
br
->
iftrue
=
doreturn
;
br
->
iffalse
=
otherwise
;
curblock
->
connectTo
(
doreturn
);
curblock
->
connectTo
(
otherwise
);
push_back
(
br
);
curblock
=
doreturn
;
doReturn
(
makeName
(
internString
(
RETURN_NAME
),
AST_TYPE
::
Load
,
node
->
lineno
));
curblock
=
otherwise
;
}
if
(
did_why
&
(
1
<<
Why
::
BREAK
))
{
CFGBlock
*
doreturn
=
cfg
->
addBlock
();
CFGBlock
*
otherwise
=
cfg
->
addBlock
();
AST_Compare
*
compare
=
new
AST_Compare
();
compare
->
ops
.
push_back
(
AST_TYPE
::
Eq
);
compare
->
left
=
makeName
(
exc_why_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
compare
->
comparators
.
push_back
(
makeNum
(
Why
::
BREAK
));
AST_Branch
*
br
=
new
AST_Branch
();
br
->
test
=
callNonzero
(
compare
);
br
->
iftrue
=
doreturn
;
br
->
iffalse
=
otherwise
;
curblock
->
connectTo
(
doreturn
);
curblock
->
connectTo
(
otherwise
);
push_back
(
br
);
curblock
=
doreturn
;
doBreak
();
curblock
=
otherwise
;
}
if
(
did_why
&
(
1
<<
Why
::
CONTINUE
))
{
CFGBlock
*
doreturn
=
cfg
->
addBlock
();
CFGBlock
*
otherwise
=
cfg
->
addBlock
();
AST_Compare
*
compare
=
new
AST_Compare
();
compare
->
ops
.
push_back
(
AST_TYPE
::
Eq
);
compare
->
left
=
makeName
(
exc_why_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
compare
->
comparators
.
push_back
(
makeNum
(
Why
::
CONTINUE
));
AST_Branch
*
br
=
new
AST_Branch
();
br
->
test
=
callNonzero
(
compare
);
br
->
iftrue
=
doreturn
;
br
->
iffalse
=
otherwise
;
curblock
->
connectTo
(
doreturn
);
curblock
->
connectTo
(
otherwise
);
push_back
(
br
);
curblock
=
doreturn
;
doContinue
();
curblock
=
otherwise
;
}
AST_Compare
*
compare
=
new
AST_Compare
();
compare
->
ops
.
push_back
(
AST_TYPE
::
Eq
);
compare
->
left
=
makeName
(
exc_why_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
compare
->
comparators
.
push_back
(
makeNum
(
Why
::
EXCEPTION
));
AST_Branch
*
br
=
new
AST_Branch
();
br
->
test
=
callNonzero
(
compare
);
CFGBlock
*
reraise
=
cfg
->
addBlock
();
CFGBlock
*
noexc
=
cfg
->
addBlock
();
br
->
iftrue
=
reraise
;
br
->
iffalse
=
noexc
;
curblock
->
connectTo
(
reraise
);
curblock
->
connectTo
(
noexc
);
push_back
(
br
);
CFGBlock
*
reraise
=
cfg
->
addDeferredBlock
();
CFGBlock
*
noexc
=
makeFinallyCont
(
Why
::
EXCEPTION
,
makeLoad
(
exc_why_name
,
node
),
reraise
);
cfg
->
placeBlock
(
reraise
);
curblock
=
reraise
;
AST_Raise
*
raise
=
new
AST_Raise
();
raise
->
arg0
=
makeName
(
exc_type_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
raise
->
arg1
=
makeName
(
exc_value_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
raise
->
arg2
=
makeName
(
exc_traceback_name
,
AST_TYPE
::
Load
,
node
->
lineno
);
push_back
(
raise
);
pushReraise
(
node
,
exc_type_name
,
exc_value_name
,
exc_traceback_name
);
curblock
=
noexc
;
}
...
...
@@ -2205,105 +2173,137 @@ public:
}
bool
visit_with
(
AST_With
*
node
)
override
{
char
ctxmgrname_buf
[
80
];
snprintf
(
ctxmgrname_buf
,
80
,
"#ctxmgr_%p"
,
node
);
InternedString
ctxmgrname
=
internString
(
ctxmgrname_buf
);
char
exitname_buf
[
80
];
snprintf
(
exitname_buf
,
80
,
"#exit_%p"
,
node
);
InternedString
exitname
=
internString
(
exitname_buf
);
// see https://www.python.org/dev/peps/pep-0343/
// section "Specification: the 'with' Statement"
// which contains pseudocode for what this implements:
//
// mgr = (EXPR)
// exit = type(mgr).__exit__ # not calling it yet
// value = type(mgr).__enter__(mgr)
// exc = True
// try:
// VAR = value
// BLOCK
// except:
// exc = False
// if not exit(mgr, *sys.exc_info()):
// raise
// finally:
// if exc:
// exit(mgr, None, None, None)
//
// Unfortunately, this pseudocode isn't *quite* correct. We don't actually call type(mgr).__exit__ and
// type(mgr).__enter__; rather, we use Python's "special method lookup rules" to find the appropriate method.
// See https://docs.python.org/2/reference/datamodel.html#new-style-special-lookup. This is one reason we can't
// just translate this into AST_Try{Except,Finally} nodes and recursively visit those. (If there are other
// reasons, I've forgotten them.)
InternedString
ctxmgrname
=
nodeName
(
node
,
"ctxmgr"
);
InternedString
exitname
=
nodeName
(
node
,
"exit"
);
InternedString
whyname
=
nodeName
(
node
,
"why"
);
InternedString
exc_type_name
=
nodeName
(
node
,
"exc_type"
);
InternedString
exc_value_name
=
nodeName
(
node
,
"exc_value"
);
InternedString
exc_traceback_name
=
nodeName
(
node
,
"exc_traceback"
);
InternedString
nonename
=
internString
(
"None"
);
CFGBlock
*
exit_block
=
cfg
->
addDeferredBlock
();
exit_block
->
info
=
"with_exit"
;
pushAssign
(
ctxmgrname
,
remapExpr
(
node
->
context_expr
));
AST_expr
*
enter
=
makeLoadAttribute
(
makeName
(
ctxmgrname
,
AST_TYPE
::
Load
,
node
->
lineno
),
internString
(
"__enter__"
),
true
);
AST_expr
*
exit
=
makeLoadAttribute
(
makeName
(
ctxmgrname
,
AST_TYPE
::
Load
,
node
->
lineno
),
internString
(
"__exit__"
),
true
);
// TODO(rntz): for some reason, in the interpreter (but not the JIT), this is looking up __exit__ on the
// instance rather than the class. See test/tests/with_ctxclass_instance_attrs.py.
AST_expr
*
exit
=
makeLoadAttribute
(
makeLoad
(
ctxmgrname
,
node
),
internString
(
"__exit__"
),
true
);
pushAssign
(
exitname
,
exit
);
enter
=
remapExpr
(
makeCall
(
enter
));
if
(
node
->
optional_vars
)
{
// Oddly, this acces to __enter__ doesn't suffer from the same bug. Perhaps it has something to do with
// __enter__ being called immediately?
AST_expr
*
enter
=
makeLoadAttribute
(
makeLoad
(
ctxmgrname
,
node
),
internString
(
"__enter__"
),
true
);
enter
=
remapExpr
(
makeCall
(
enter
));
if
(
node
->
optional_vars
)
pushAssign
(
node
->
optional_vars
,
enter
);
}
else
{
else
push_back
(
makeExpr
(
enter
));
}
CFGBlock
*
continue_dest
=
NULL
,
*
break_dest
=
NULL
;
if
(
regions
.
size
())
{
continue_dest
=
cfg
->
addDeferredBlock
();
continue_dest
->
info
=
"with_continue"
;
break_dest
=
cfg
->
addDeferredBlock
();
break_dest
->
info
=
"with_break"
;
pushLoopRegion
(
continue_dest
,
break_dest
);
}
// push continuations
CFGBlock
*
finally_block
=
cfg
->
addDeferredBlock
();
finally_block
->
info
=
"with_finally"
;
pushFinallyContinuation
(
finally_block
,
whyname
);
CFGBlock
*
return_dest
=
cfg
->
addDeferredBlock
();
return_dest
->
info
=
"with_return
"
;
pushReturnRegion
(
return_dest
);
CFGBlock
*
exc_block
=
cfg
->
addDeferredBlock
();
exc_block
->
info
=
"with_exc
"
;
exc_handlers
.
push_back
({
exc_block
,
exc_type_name
,
exc_value_name
,
exc_traceback_name
}
);
for
(
int
i
=
0
;
i
<
node
->
body
.
size
();
i
++
)
{
node
->
body
[
i
]
->
accept
(
this
);
}
popRegion
();
// for the retrun
AST_Call
*
exit_call
=
makeCall
(
makeName
(
exitname
,
AST_TYPE
::
Load
,
node
->
lineno
));
exit_call
->
args
.
push_back
(
makeName
(
nonename
,
AST_TYPE
::
Load
,
node
->
lineno
));
exit_call
->
args
.
push_back
(
makeName
(
nonename
,
AST_TYPE
::
Load
,
node
->
lineno
));
exit_call
->
args
.
push_back
(
makeName
(
nonename
,
AST_TYPE
::
Load
,
node
->
lineno
));
push_back
(
makeExpr
(
exit_call
));
CFGBlock
*
orig_ending_block
=
curblock
;
if
(
continue_dest
)
{
popRegion
();
// for the loop region
if
(
continue_dest
->
predecessors
.
size
()
==
0
)
{
delete
continue_dest
;
}
else
{
curblock
=
continue_dest
;
AST_Call
*
exit_call
=
makeCall
(
makeName
(
exitname
,
AST_TYPE
::
Load
,
node
->
lineno
));
exit_call
->
args
.
push_back
(
makeName
(
nonename
,
AST_TYPE
::
Load
,
node
->
lineno
));
exit_call
->
args
.
push_back
(
makeName
(
nonename
,
AST_TYPE
::
Load
,
node
->
lineno
));
exit_call
->
args
.
push_back
(
makeName
(
nonename
,
AST_TYPE
::
Load
,
node
->
lineno
));
push_back
(
makeExpr
(
exit_call
));
exc_handlers
.
pop_back
();
int
finally_did_why
=
continuations
.
back
().
did_why
;
popContinuation
();
cfg
->
placeBlock
(
continue_dest
);
doContinue
();
if
(
curblock
)
{
// The try-suite finished as normal; jump to the finally block.
pushAssign
(
whyname
,
makeNum
(
Why
::
FALLTHROUGH
));
pushJump
(
finally_block
);
}
if
(
break_dest
->
predecessors
.
size
()
==
0
)
{
delete
break_dest
;
// The exception-handling block
if
(
exc_block
->
predecessors
.
size
()
==
0
)
{
// TODO(rntz): test for this case
delete
exc_block
;
}
else
{
curblock
=
break_dest
;
AST_Call
*
exit_call
=
makeCall
(
makeName
(
exitname
,
AST_TYPE
::
Load
,
node
->
lineno
));
exit_call
->
args
.
push_back
(
makeName
(
nonename
,
AST_TYPE
::
Load
,
node
->
lineno
));
exit_call
->
args
.
push_back
(
makeName
(
nonename
,
AST_TYPE
::
Load
,
node
->
lineno
));
exit_call
->
args
.
push_back
(
makeName
(
nonename
,
AST_TYPE
::
Load
,
node
->
lineno
));
push_back
(
makeExpr
(
exit_call
));
cfg
->
placeBlock
(
break_dest
);
doBreak
();
}
curblock
=
orig_ending_block
;
}
if
(
return_dest
->
predecessors
.
size
()
==
0
)
{
delete
return_dest
;
cfg
->
placeBlock
(
exc_block
);
curblock
=
exc_block
;
// call the context-manager's exit method
InternedString
suppressname
=
nodeName
(
node
,
"suppress"
);
pushAssign
(
suppressname
,
makeCall
(
makeLoad
(
exitname
,
node
),
makeLoad
(
exc_type_name
,
node
),
makeLoad
(
exc_value_name
,
node
),
makeLoad
(
exc_traceback_name
,
node
)));
// if it returns true, suppress the error and go to our exit block
CFGBlock
*
reraise_block
=
cfg
->
addDeferredBlock
();
reraise_block
->
info
=
"with_reraise"
;
// break potential critical edge
CFGBlock
*
exiter
=
cfg
->
addDeferredBlock
();
exiter
->
info
=
"with_exiter"
;
pushBranch
(
makeLoad
(
suppressname
,
node
),
exiter
,
reraise_block
);
cfg
->
placeBlock
(
exiter
);
curblock
=
exiter
;
pushJump
(
exit_block
);
// otherwise, reraise the exception
cfg
->
placeBlock
(
reraise_block
);
curblock
=
reraise_block
;
pushReraise
(
node
,
exc_type_name
,
exc_value_name
,
exc_traceback_name
);
}
// The finally block
if
(
finally_block
->
predecessors
.
size
()
==
0
)
{
// TODO(rntz): test for this case, "with foo: raise bar"
delete
finally_block
;
}
else
{
cfg
->
placeBlock
(
return_dest
);
curblock
=
return_dest
;
AST_Call
*
exit_call
=
makeCall
(
makeName
(
exitname
,
AST_TYPE
::
Load
,
node
->
lineno
));
exit_call
->
args
.
push_back
(
makeName
(
nonename
,
AST_TYPE
::
Load
,
node
->
lineno
));
exit_call
->
args
.
push_back
(
makeName
(
nonename
,
AST_TYPE
::
Load
,
node
->
lineno
));
exit_call
->
args
.
push_back
(
makeName
(
nonename
,
AST_TYPE
::
Load
,
node
->
lineno
));
push_back
(
makeExpr
(
exit_call
));
doReturn
(
makeName
(
internString
(
RETURN_NAME
),
AST_TYPE
::
Load
,
node
->
lineno
));
curblock
=
orig_ending_block
;
cfg
->
placeBlock
(
finally_block
);
curblock
=
finally_block
;
// call the context-manager's exit method, ignoring result
push_back
(
makeExpr
(
makeCall
(
makeLoad
(
exitname
,
node
),
makeLoad
(
nonename
,
node
),
makeLoad
(
nonename
,
node
),
makeLoad
(
nonename
,
node
))));
if
(
finally_did_why
&
(
1
<<
Why
::
CONTINUE
))
exitFinallyIf
(
node
,
Why
::
CONTINUE
,
whyname
);
if
(
finally_did_why
&
(
1
<<
Why
::
BREAK
))
exitFinallyIf
(
node
,
Why
::
BREAK
,
whyname
);
if
(
finally_did_why
&
(
1
<<
Why
::
RETURN
))
exitFinallyIf
(
node
,
Why
::
RETURN
,
whyname
);
exitFinally
(
node
,
Why
::
FALLTHROUGH
,
exit_block
);
}
if
(
exit_block
->
predecessors
.
size
()
==
0
)
{
// FIXME(rntz): does this ever happen?
// make a test for it!
delete
exit_block
;
}
else
{
cfg
->
placeBlock
(
exit_block
);
curblock
=
exit_block
;
}
return
true
;
...
...
@@ -2313,30 +2313,8 @@ public:
void
CFG
::
print
()
{
printf
(
"CFG:
\n
"
);
printf
(
"%ld blocks
\n
"
,
blocks
.
size
());
PrintVisitor
*
pv
=
new
PrintVisitor
(
4
);
for
(
int
i
=
0
;
i
<
blocks
.
size
();
i
++
)
{
CFGBlock
*
b
=
blocks
[
i
];
printf
(
"Block %d"
,
b
->
idx
);
if
(
b
->
info
)
printf
(
" '%s'"
,
b
->
info
);
printf
(
"; Predecessors:"
);
for
(
int
j
=
0
;
j
<
b
->
predecessors
.
size
();
j
++
)
{
printf
(
" %d"
,
b
->
predecessors
[
j
]
->
idx
);
}
printf
(
" Successors:"
);
for
(
int
j
=
0
;
j
<
b
->
successors
.
size
();
j
++
)
{
printf
(
" %d"
,
b
->
successors
[
j
]
->
idx
);
}
printf
(
"
\n
"
);
for
(
int
j
=
0
;
j
<
b
->
body
.
size
();
j
++
)
{
printf
(
" "
);
b
->
body
[
j
]
->
accept
(
pv
);
printf
(
"
\n
"
);
}
}
delete
pv
;
for
(
int
i
=
0
;
i
<
blocks
.
size
();
i
++
)
blocks
[
i
]
->
print
();
}
CFG
*
computeCFG
(
SourceInfo
*
source
,
std
::
vector
<
AST_stmt
*>
body
)
{
...
...
src/core/cfg.h
View file @
7fb7aaa5
...
...
@@ -56,6 +56,7 @@ public:
void
unconnectFrom
(
CFGBlock
*
successor
);
void
push_back
(
AST_stmt
*
node
)
{
body
.
push_back
(
node
);
}
void
print
();
};
// Control Flow Graph
...
...
@@ -79,6 +80,9 @@ public:
return
block
;
}
// Creates a block which must be placed later, using placeBlock().
// Must be placed on same CFG it was created on.
// You can also safely delete it without placing it.
CFGBlock
*
addDeferredBlock
()
{
CFGBlock
*
block
=
new
CFGBlock
(
this
,
-
1
);
return
block
;
...
...
src/core/common.h
View file @
7fb7aaa5
...
...
@@ -39,6 +39,8 @@
#define _CAT(A, B) A##B
#define CAT(A, B) _CAT(A, B)
#define ARRAY_LEN(arr) (sizeof(arr) / sizeof((arr)[0]))
// GCC and clang handle always_inline very differently;
// we mostly only care about it for the stdlib, so just remove the attributes
// if we're not in clang
...
...
src/core/util.h
View file @
7fb7aaa5
...
...
@@ -52,7 +52,9 @@ bool endswith(const std::string& s, const std::string& pattern);
void
removeDirectoryIfExists
(
const
std
::
string
&
path
);
template
<
class
T1
,
class
T2
>
void
compareKeyset
(
T1
*
lhs
,
T2
*
rhs
)
{
// Checks that lhs and rhs, which are iterables of InternedStrings, have the
// same set of names in them.
template
<
class
T1
,
class
T2
>
bool
sameKeyset
(
T1
*
lhs
,
T2
*
rhs
)
{
std
::
vector
<
InternedString
>
lv
,
rv
;
for
(
typename
T1
::
iterator
it
=
lhs
->
begin
();
it
!=
lhs
->
end
();
it
++
)
{
lv
.
push_back
(
it
->
first
);
...
...
@@ -89,7 +91,7 @@ template <class T1, class T2> void compareKeyset(T1* lhs, T2* rhs) {
}
good
=
false
;
}
assert
(
good
)
;
return
good
;
}
}
...
...
src/runtime/types.cpp
View file @
7fb7aaa5
...
...
@@ -366,6 +366,9 @@ std::string BoxedModule::name() {
}
}
// This mustn't throw; our IR generator generates calls to it without "invoke" even when there are exception handlers /
// finally-blocks in scope.
// TODO: should we use C++11 `noexcept' here?
extern
"C"
Box
*
boxCLFunction
(
CLFunction
*
f
,
BoxedClosure
*
closure
,
bool
isGenerator
,
std
::
initializer_list
<
Box
*>
defaults
)
{
if
(
closure
)
...
...
test/tests/exceptions_test.py
View file @
7fb7aaa5
# fail-if: '-n' in EXTRA_JIT_ARGS or '-O' in EXTRA_JIT_ARGS
# we have an llvm codegen bug that this file triggers when we JIT
class
TestException
(
Exception
):
pass
...
...
test/tests/sys_test.py
View file @
7fb7aaa5
# expected: fail
# allow-warning: converting unicode literal to str
import
sys
...
...
test/tests/try_class.py
View file @
7fb7aaa5
...
...
@@ -27,5 +27,6 @@ def f2():
print
'here'
except
:
print
'impossible'
print
D
raise
f2
()
test/tests/try_def.py
View file @
7fb7aaa5
# fail-if: (('-O' in EXTRA_JIT_ARGS) or ('-n' in EXTRA_JIT_ARGS)) and 'release' not in IMAGE
def
f
():
try
:
def
foo
():
return
0
...
...
test/tests/with_class_redefine.py
View file @
7fb7aaa5
#
expected: fail
#
fail-if: (('-O' in EXTRA_JIT_ARGS) or ('-n' in EXTRA_JIT_ARGS)) and 'release' not in IMAGE
def
f
():
C
=
23
try
:
...
...
test/tests/with_ctxclass_order_of_access.py
View file @
7fb7aaa5
...
...
@@ -5,7 +5,7 @@ class Mgr(object):
def
__enter__
(
self
):
print
'Mgr.__enter__ accessed'
def
enterer
(
*
args
):
print
'Mgr.__enter__
%r called'
%
(
args
,)
print
'Mgr.__enter__
called'
return
23
return
enterer
...
...
@@ -13,7 +13,7 @@ class Mgr(object):
def
__exit__
(
self
):
print
'Mgr.__exit__ accessed'
def
exiter
(
*
args
):
print
'Mgr.__exit__
%r called'
%
(
args
,)
print
'Mgr.__exit__
called'
return
False
return
exiter
...
...
test/tests/with_functiondef.py
View file @
7fb7aaa5
# fail-if: (('-O' in EXTRA_JIT_ARGS) or ('-n' in EXTRA_JIT_ARGS)) and 'release' not in IMAGE
def
f
():
#
originally this exposed a bug in our irgen phase, so even `with None`
#
failed here; the bug happened before actual execution. Just to test more
#
things, though,
we use an actual contextmanager here.
#
this exposes a bug in our irgen phase, so even `with None` bugs out here;
#
the bug happens before actual execution. Just to test more things, though,
# we use an actual contextmanager here.
with
open
(
'/dev/null'
):
def
foo
():
# raises a syntaxerror
pass
f
()
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