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
50eb5bed
Commit
50eb5bed
authored
Jul 31, 2014
by
Chris Ramstad
Browse files
Options
Browse Files
Download
Plain Diff
Merge remote-tracking branch 'upstream/master'
parents
d0863da3
e185374c
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
1771 additions
and
2087 deletions
+1771
-2087
src/asm_writing/assembler.cpp
src/asm_writing/assembler.cpp
+0
-1
src/asm_writing/rewriter.cpp
src/asm_writing/rewriter.cpp
+806
-280
src/asm_writing/rewriter.h
src/asm_writing/rewriter.h
+228
-83
src/asm_writing/rewriter2.cpp
src/asm_writing/rewriter2.cpp
+0
-773
src/asm_writing/rewriter2.h
src/asm_writing/rewriter2.h
+0
-271
src/core/cfg.cpp
src/core/cfg.cpp
+1
-1
src/core/types.h
src/core/types.h
+11
-7
src/runtime/builtin_modules/builtins.cpp
src/runtime/builtin_modules/builtins.cpp
+3
-3
src/runtime/objmodel.cpp
src/runtime/objmodel.cpp
+715
-660
src/runtime/objmodel.h
src/runtime/objmodel.h
+4
-5
src/runtime/types.cpp
src/runtime/types.cpp
+3
-3
No files found.
src/asm_writing/assembler.cpp
View file @
50eb5bed
...
...
@@ -477,7 +477,6 @@ void Assembler::cmp(Register reg1, Register reg2) {
reg1_idx
-=
8
;
}
if
(
reg2_idx
>=
8
)
{
trap
();
rex
|=
REX_B
;
reg2_idx
-=
8
;
}
...
...
src/asm_writing/rewriter.cpp
View file @
50eb5bed
...
...
@@ -14,425 +14,680 @@
#include "asm_writing/rewriter.h"
#include "asm_writing/assembler.h"
#include <vector>
#include "asm_writing/icinfo.h"
#include "core/common.h"
#include "core/stats.h"
namespace
pyston
{
using
namespace
pyston
::
assembler
;
#define MAX_ARGS 16
static
const
assembler
::
Register
allocatable_regs
[]
=
{
assembler
::
RAX
,
assembler
::
RCX
,
assembler
::
RBX
,
assembler
::
RDX
,
// no RSP
// no RBP
assembler
::
RDI
,
assembler
::
RSI
,
assembler
::
R8
,
assembler
::
R9
,
assembler
::
R10
,
assembler
::
R11
,
assembler
::
R12
,
assembler
::
R13
,
assembler
::
R14
,
assembler
::
R15
,
};
Register
fromArgnum
(
int
argnum
)
{
Location
Location
::
forArg
(
int
argnum
)
{
assert
(
argnum
>=
0
);
switch
(
argnum
)
{
case
-
5
:
return
RBP
;
case
-
4
:
return
RSP
;
case
-
3
:
return
R11
;
case
-
2
:
return
R10
;
case
-
1
:
return
RAX
;
case
0
:
return
RDI
;
return
assembler
::
RDI
;
case
1
:
return
RSI
;
return
assembler
::
RSI
;
case
2
:
return
RDX
;
return
assembler
::
RDX
;
case
3
:
return
RCX
;
return
assembler
::
RCX
;
case
4
:
return
R8
;
return
assembler
::
R8
;
case
5
:
return
R9
;
return
assembler
::
R9
;
default:
break
;
}
RELEASE_ASSERT
(
0
,
"%d"
,
argnum
);
int
offset
=
(
argnum
-
6
)
*
8
;
return
Location
(
Stack
,
offset
);
}
RewriterVar
::
RewriterVar
(
Rewriter
*
rewriter
,
int
argnum
,
int
version
)
:
rewriter
(
rewriter
),
argnum
(
argnum
),
version
(
version
)
{
// assert(rewriter.icentry.get()
);
assembler
::
Register
Location
::
asRegister
()
const
{
assert
(
type
==
Register
);
return
assembler
::
Register
(
regnum
);
}
RewriterVar
&
RewriterVar
::
operator
=
(
const
RewriterVar
&
rhs
)
{
assert
(
rewriter
==
NULL
||
rewriter
==
rhs
.
rewriter
);
rhs
.
assertValid
();
rewriter
=
rhs
.
rewriter
;
argnum
=
rhs
.
argnum
;
version
=
rhs
.
version
;
return
*
this
;
assembler
::
XMMRegister
Location
::
asXMMRegister
()
const
{
assert
(
type
==
XMMRegister
);
return
assembler
::
XMMRegister
(
regnum
);
}
#ifndef NDEBUG
void
RewriterVar
::
assertValid
()
const
{
assert
(
rewriter
);
rewriter
->
checkVersion
(
argnum
,
version
);
}
bool
Location
::
isClobberedByCall
()
const
{
if
(
type
==
Register
)
{
return
!
asRegister
().
isCalleeSave
();
}
void
RewriterVar
::
lock
()
{
assertValid
();
rewriter
->
lock
(
argnum
);
}
if
(
type
==
XMMRegister
)
return
true
;
void
RewriterVar
::
unlock
()
{
assertValid
();
rewriter
->
unlock
(
argnum
);
}
#endif
if
(
type
==
Scratch
)
return
false
;
int
RewriterVar
::
getArgnum
()
{
// assert(rewriter);
return
argnum
;
}
if
(
type
==
Constant
)
return
false
;
RewriterVar
RewriterVar
::
getAttr
(
int
offset
,
int
dest
)
{
assertValid
()
;
if
(
type
==
Stack
)
return
false
;
rewriter
->
assembler
->
mov
(
Indirect
(
fromArgnum
(
this
->
argnum
),
offset
),
fromArgnum
(
dest
));
int
version
=
rewriter
->
mutate
(
dest
);
return
RewriterVar
(
rewriter
,
dest
,
version
);
RELEASE_ASSERT
(
0
,
"%d"
,
type
);
}
void
RewriterVar
::
incAttr
(
int
offset
)
{
assertValid
();
void
Location
::
dump
()
const
{
if
(
type
==
Register
)
{
asRegister
().
dump
();
return
;
}
rewriter
->
assembler
->
inc
(
Indirect
(
fromArgnum
(
this
->
argnum
),
offset
));
if
(
type
==
XMMRegister
)
{
printf
(
"%%xmm%d
\n
"
,
regnum
);
return
;
}
#ifndef NDEBUG
rewriter
->
changed_something
=
true
;
#endif
}
if
(
type
==
Scratch
)
{
printf
(
"scratch(%d)
\n
"
,
scratch_offset
)
;
return
;
}
void
RewriterVar
::
setAttr
(
int
offset
,
const
RewriterVar
&
val
,
bool
user_visible
)
{
assertValid
();
val
.
assertValid
();
if
(
type
==
Constant
)
{
printf
(
"imm(%d)
\n
"
,
constant_val
);
return
;
}
rewriter
->
assembler
->
mov
(
fromArgnum
(
val
.
argnum
),
Indirect
(
fromArgnum
(
this
->
argnum
),
offset
));
if
(
type
==
Stack
)
{
printf
(
"stack(%d)
\n
"
,
stack_offset
);
return
;
}
#ifndef NDEBUG
if
(
user_visible
)
rewriter
->
changed_something
=
true
;
#endif
RELEASE_ASSERT
(
0
,
"%d"
,
type
);
}
RewriterVar
RewriterVar
::
move
(
int
dest_argnum
)
{
RewriterVarUsage
::
RewriterVarUsage
(
RewriterVar
*
var
)
:
var
(
var
),
done_using
(
false
)
{
var
->
incUse
();
assert
(
var
->
rewriter
);
}
void
RewriterVarUsage
::
addGuard
(
uint64_t
val
)
{
assertValid
();
int
version
;
if
(
dest_argnum
!=
this
->
argnum
)
{
assert
(
dest_argnum
<
6
);
Rewriter
*
rewriter
=
var
->
rewriter
;
assembler
::
Assembler
*
assembler
=
rewriter
->
assembler
;
if
(
this
->
argnum
>=
6
)
{
if
(
1
)
{
int
offset
=
(
this
->
argnum
-
6
)
*
8
+
rewriter
->
pushes
.
size
()
*
8
+
rewriter
->
alloca_bytes
;
rewriter
->
assembler
->
mov
(
Indirect
(
RSP
,
offset
),
fromArgnum
(
dest_argnum
));
}
else
{
int
stack_size
=
rewriter
->
rewrite
->
getFuncStackSize
();
ASSERT
(
stack_size
>
0
&&
stack_size
<
(
1
<<
30
),
"%d"
,
stack_size
);
int
offset
=
(
this
->
argnum
-
6
)
*
8
-
(
stack_size
-
8
);
rewriter
->
assembler
->
mov
(
Indirect
(
RBP
,
offset
),
fromArgnum
(
dest_argnum
));
}
}
else
{
rewriter
->
assembler
->
mov
(
fromArgnum
(
this
->
argnum
),
fromArgnum
(
dest_argnum
));
}
assert
(
!
rewriter
->
done_guarding
&&
"too late to add a guard!"
);
version
=
rewriter
->
mutate
(
dest_argnum
);
assembler
::
Register
this_reg
=
var
->
getInReg
();
if
(
val
<
(
-
1L
<<
31
)
||
val
>=
(
1L
<<
31
)
-
1
)
{
assembler
::
Register
reg
=
rewriter
->
allocReg
(
Location
::
any
());
assembler
->
mov
(
assembler
::
Immediate
(
val
),
reg
);
assembler
->
cmp
(
this_reg
,
reg
);
}
else
{
version
=
this
->
version
;
assembler
->
cmp
(
this_reg
,
assembler
::
Immediate
(
val
))
;
}
return
RewriterVar
(
rewriter
,
dest_argnum
,
version
);
assembler
->
jne
(
assembler
::
JumpDestination
::
fromStart
(
rewriter
->
rewrite
->
getSlotSize
())
);
}
void
RewriterVar
::
addGuard
(
intptr_t
val
)
{
assert
(
!
rewriter
->
changed_something
&&
"too late to add a guard!"
);
void
RewriterVarUsage
::
addGuardNotEq
(
uint64_t
val
)
{
assertValid
();
rewriter
->
checkArgsValid
();
Rewriter
*
rewriter
=
var
->
rewriter
;
assembler
::
Assembler
*
assembler
=
rewriter
->
assembler
;
int
bytes
=
8
*
rewriter
->
pushes
.
size
()
+
rewriter
->
alloca_bytes
;
assert
(
!
rewriter
->
done_guarding
&&
"too late to add a guard!"
)
;
assembler
::
Register
this_reg
=
var
->
getInReg
();
if
(
val
<
(
-
1L
<<
31
)
||
val
>=
(
1L
<<
31
)
-
1
)
{
rewriter
->
assembler
->
push
(
RBP
);
rewriter
->
assembler
->
mov
(
Immediate
(
val
),
RBP
);
rewriter
->
assembler
->
cmp
(
fromArgnum
(
this
->
argnum
),
RBP
);
rewriter
->
assembler
->
pop
(
RBP
);
assembler
::
Register
reg
=
rewriter
->
allocReg
(
Location
::
any
());
assembler
->
mov
(
assembler
::
Immediate
(
val
),
reg
);
assembler
->
cmp
(
this_reg
,
reg
);
}
else
{
rewriter
->
assembler
->
cmp
(
fromArgnum
(
this
->
argnum
),
Immediate
(
val
));
assembler
->
cmp
(
this_reg
,
assembler
::
Immediate
(
val
));
}
rewriter
->
assembler
->
jne
(
JumpDestination
::
fromStart
(
rewriter
->
rewrite
->
getSlotSize
()
-
bytes
/
8
));
assembler
->
je
(
assembler
::
JumpDestination
::
fromStart
(
rewriter
->
rewrite
->
getSlotSize
()
));
}
void
RewriterVar
::
addAttrGuard
(
int
offset
,
intptr_t
val
)
{
assert
(
!
rewriter
->
changed_something
&&
"too late to add a guard!"
);
void
RewriterVarUsage
::
addAttrGuard
(
int
offset
,
uint64_t
val
)
{
assertValid
();
rewriter
->
checkArgsValid
();
Rewriter
*
rewriter
=
var
->
rewriter
;
assembler
::
Assembler
*
assembler
=
rewriter
->
assembler
;
int
bytes
=
8
*
rewriter
->
pushes
.
size
()
+
rewriter
->
alloca_bytes
;
assert
(
!
rewriter
->
done_guarding
&&
"too late to add a guard!"
)
;
assembler
::
Register
this_reg
=
var
->
getInReg
();
if
(
val
<
(
-
1L
<<
31
)
||
val
>=
(
1L
<<
31
)
-
1
)
{
rewriter
->
assembler
->
push
(
RBP
);
rewriter
->
assembler
->
mov
(
Immediate
(
val
),
RBP
);
rewriter
->
assembler
->
cmp
(
Indirect
(
fromArgnum
(
this
->
argnum
),
offset
),
RBP
);
rewriter
->
assembler
->
pop
(
RBP
);
assembler
::
Register
reg
=
rewriter
->
allocReg
(
Location
::
any
()
);
assert
(
reg
!=
this_reg
);
assembler
->
mov
(
assembler
::
Immediate
(
val
),
reg
);
assembler
->
cmp
(
assembler
::
Indirect
(
this_reg
,
offset
),
reg
);
}
else
{
rewriter
->
assembler
->
cmp
(
Indirect
(
fromArgnum
(
this
->
argnum
),
offset
),
Immediate
(
val
));
assembler
->
cmp
(
assembler
::
Indirect
(
this_reg
,
offset
),
assembler
::
Immediate
(
val
));
}
rewriter
->
assembler
->
jne
(
JumpDestination
::
fromStart
(
rewriter
->
rewrite
->
getSlotSize
()
-
bytes
/
8
));
assembler
->
jne
(
assembler
::
JumpDestination
::
fromStart
(
rewriter
->
rewrite
->
getSlotSize
()
));
}
void
RewriterVar
::
addGuardNotEq
(
intptr_t
val
)
{
assert
(
!
rewriter
->
changed_something
&&
"too late to add a guard!"
);
RewriterVarUsage
RewriterVarUsage
::
getAttr
(
int
offset
,
KillFlag
kill
,
Location
dest
)
{
assertValid
();
rewriter
->
checkArgsValid
();
// Save these, since if we kill this register the var might disappear:
assembler
::
Register
this_reg
=
var
->
getInReg
();
Rewriter
*
rewriter
=
var
->
rewriter
;
int
bytes
=
8
*
rewriter
->
pushes
.
size
()
+
rewriter
->
alloca_bytes
;
rewriter
->
assembler
->
cmp
(
fromArgnum
(
this
->
argnum
),
Immediate
(
val
));
rewriter
->
assembler
->
je
(
JumpDestination
::
fromStart
(
rewriter
->
rewrite
->
getSlotSize
()
-
bytes
/
8
));
}
if
(
kill
==
Kill
)
{
setDoneUsing
();
}
bool
RewriterVar
::
isInReg
()
{
int
num_arg_regs
=
6
;
return
argnum
<
num_arg_regs
;
assembler
::
Register
newvar_reg
=
rewriter
->
allocReg
(
dest
);
RewriterVarUsage
newvar
=
rewriter
->
createNewVar
(
newvar_reg
);
rewriter
->
assembler
->
mov
(
assembler
::
Indirect
(
this_reg
,
offset
),
newvar_reg
);
return
std
::
move
(
newvar
);
}
void
RewriterVar
::
push
(
)
{
RewriterVarUsage
RewriterVarUsage
::
cmp
(
AST_TYPE
::
AST_TYPE
cmp_type
,
RewriterVarUsage
other
,
Location
dest
)
{
assertValid
();
assert
(
isInReg
());
rewriter
->
assembler
->
push
(
fromArgnum
(
this
->
argnum
));
rewriter
->
addPush
(
this
->
version
);
}
RewriterVar
RewriterVar
::
cmp
(
AST_TYPE
::
AST_TYPE
cmp_type
,
const
RewriterVar
&
val
,
int
dest
)
{
assertValid
();
assembler
::
Register
this_reg
=
var
->
getInReg
();
assembler
::
Register
other_reg
=
other
.
var
->
getInReg
();
assert
(
this_reg
!=
other_reg
);
// TODO how do we ensure this?
Rewriter
*
rewriter
=
var
->
rewriter
;
rewriter
->
assembler
->
cmp
(
fromArgnum
(
this
->
argnum
),
fromArgnum
(
val
.
argnum
));
assembler
::
Register
newvar_reg
=
rewriter
->
allocReg
(
dest
);
rewriter
->
assembler
->
cmp
(
this_reg
,
other_reg
);
RewriterVarUsage
newvar
=
rewriter
->
createNewVar
(
newvar_reg
);
switch
(
cmp_type
)
{
case
AST_TYPE
:
:
Eq
:
rewriter
->
assembler
->
sete
(
fromArgnum
(
dest
)
);
rewriter
->
assembler
->
sete
(
newvar_reg
);
break
;
case
AST_TYPE
:
:
NotEq
:
rewriter
->
assembler
->
setne
(
fromArgnum
(
dest
)
);
rewriter
->
assembler
->
setne
(
newvar_reg
);
break
;
default:
RELEASE_ASSERT
(
0
,
"%d"
,
cmp_type
);
}
int
version
=
rewriter
->
mutate
(
dest
);
return
RewriterVar
(
rewriter
,
dest
,
version
);
other
.
setDoneUsing
();
return
std
::
move
(
newvar
);
}
RewriterVar
RewriterVar
::
toBool
(
int
dest
)
{
RewriterVar
Usage
RewriterVarUsage
::
toBool
(
KillFlag
kill
,
Location
dest
)
{
assertValid
();
rewriter
->
assembler
->
test
(
fromArgnum
(
this
->
argnum
),
fromArgnum
(
this
->
argnum
));
rewriter
->
assembler
->
setnz
(
fromArgnum
(
dest
));
assembler
::
Register
this_reg
=
var
->
getInReg
();
Rewriter
*
rewriter
=
var
->
rewriter
;
if
(
kill
==
Kill
)
{
setDoneUsing
();
}
rewriter
->
assembler
->
test
(
this_reg
,
this_reg
);
assembler
::
Register
result_reg
=
rewriter
->
allocReg
(
dest
);
rewriter
->
assembler
->
setnz
(
result_reg
);
int
version
=
rewriter
->
mutate
(
dest
);
return
RewriterVar
(
rewriter
,
dest
,
version
)
;
RewriterVarUsage
result
=
rewriter
->
createNewVar
(
result_reg
);
return
result
;
}
RewriterVar
RewriterVar
::
add
(
int64_t
amount
)
{
void
RewriterVarUsage
::
setAttr
(
int
offset
,
RewriterVarUsage
val
)
{
assertValid
();
var
->
rewriter
->
assertChangesOk
();
if
(
amount
>
0
)
rewriter
->
assembler
->
add
(
assembler
::
Immediate
(
amount
),
fromArgnum
(
this
->
argnum
));
else
rewriter
->
assembler
->
sub
(
assembler
::
Immediate
(
-
amount
),
fromArgnum
(
this
->
argnum
));
int
new_version
=
rewriter
->
mutate
(
this
->
argnum
);
return
RewriterVar
(
rewriter
,
this
->
argnum
,
new_version
);
}
assembler
::
Register
this_reg
=
var
->
getInReg
();
Rewriter
*
Rewriter
::
createRewriter
(
void
*
ic_rtn_addr
,
int
num_orig_args
,
int
num_temp_regs
,
const
char
*
debug_name
)
{
asse
rt
(
num_temp_regs
<=
2
&&
"unsupported"
);
bool
is_immediate
;
asse
mbler
::
Immediate
imm
=
val
.
var
->
tryGetAsImmediate
(
&
is_immediate
);
static
StatCounter
rewriter_nopatch
(
"rewriter_nopatch"
);
if
(
is_immediate
)
{
var
->
rewriter
->
assembler
->
movq
(
imm
,
assembler
::
Indirect
(
this_reg
,
offset
));
}
else
{
assembler
::
Register
other_reg
=
val
.
var
->
getInReg
();
ICInfo
*
ic
=
getICInfo
(
ic_rtn_addr
);
if
(
ic
==
NULL
)
{
rewriter_nopatch
.
log
();
return
NULL
;
// TODO the allocator could choose to spill this_reg in order to load other_reg...
// Hopefuly it won't make that decision, so we should just be able to guard on it for now:
assert
(
this_reg
!=
other_reg
);
var
->
rewriter
->
assembler
->
mov
(
other_reg
,
assembler
::
Indirect
(
this_reg
,
offset
));
}
assert
(
ic
->
getCallingConvention
()
==
llvm
::
CallingConv
::
C
&&
"Rewriter[1] only supports the C calling convention!"
);
return
new
Rewriter
(
ic
->
startRewrite
(
debug_name
),
num_orig_args
,
num_temp_regs
);
val
.
setDoneUsing
();
}
Rewriter
::
Rewriter
(
ICSlotRewrite
*
rewrite
,
int
num_orig_args
,
int
num_temp_regs
)
:
rewrite
(
rewrite
),
assembler
(
rewrite
->
getAssembler
()),
num_orig_args
(
num_orig_args
),
num_temp_regs
(
num_temp_regs
),
alloca_bytes
(
0
),
max_pushes
(
0
)
#ifndef NDEBUG
,
next_version
(
2
),
changed_something
(
false
)
#endif
,
ndecisions
(
0
),
decision_path
(
1
)
{
// printf("trapping here\n");
// assembler->trap();
void
RewriterVarUsage
::
setDoneUsing
()
{
assertValid
();
done_using
=
true
;
var
->
decUse
();
var
=
NULL
;
}
// for (int i = 0; i < num_temp_regs; i++
) {
// icentry->push(-2 - i)
;
//
}
bool
RewriterVarUsage
::
isDoneUsing
(
)
{
return
done_using
;
}
#ifndef NDEBUG
for
(
int
i
=
-
5
;
i
<
MAX_ARGS
;
i
++
)
{
versions
[
i
]
=
next_version
++
;
}
#endif
void
RewriterVarUsage
::
ensureDoneUsing
()
{
if
(
!
done_using
)
setDoneUsing
();
}
void
Rewriter
::
addPush
(
int
version
)
{
pushes
.
push_back
(
version
);
max_pushes
=
std
::
max
(
max_pushes
,
(
int
)
pushes
.
size
());
RewriterVarUsage
::
RewriterVarUsage
(
RewriterVarUsage
&&
usage
)
{
assert
(
!
usage
.
done_using
);
assert
(
usage
.
var
!=
NULL
);
var
=
usage
.
var
;
done_using
=
usage
.
done_using
;
usage
.
var
=
NULL
;
usage
.
done_using
=
true
;
}
RewriterVar
Rewriter
::
alloca_
(
int
bytes
,
int
dest_argnum
)
{
// TODO should check to make sure we aren't crossing push+pops and allocas
// printf("alloca()ing %d bytes\n", bytes);
assert
(
bytes
%
sizeof
(
void
*
)
==
0
);
alloca_bytes
+=
bytes
;
RewriterVarUsage
&
RewriterVarUsage
::
operator
=
(
RewriterVarUsage
&&
usage
)
{
assert
(
done_using
);
assert
(
var
==
NULL
);
assert
(
!
usage
.
done_using
);
assert
(
usage
.
var
!=
NULL
);
var
=
usage
.
var
;
done_using
=
usage
.
done_using
;
assembler
->
sub
(
Immediate
(
bytes
),
RSP
)
;
assembler
->
mov
(
RSP
,
fromArgnum
(
dest_argnum
))
;
usage
.
var
=
NULL
;
usage
.
done_using
=
true
;
int
version
=
mutate
(
dest_argnum
);
return
RewriterVar
(
this
,
dest_argnum
,
version
);
return
*
this
;
}
RewriterVar
Rewriter
::
getArg
(
int
argnum
)
{
assert
(
argnum
>=
-
1
);
assert
(
argnum
<
MAX_ARGS
);
#ifndef NDEBUG
int
version
=
versions
[
argnum
];
assert
(
version
);
assert
(
version
==
argnum
+
7
);
#else
int
version
=
0
;
#endif
return
RewriterVar
(
this
,
argnum
,
version
);
void
RewriterVar
::
dump
()
{
printf
(
"RewriterVar at %p: %d uses. %ld locations:
\n
"
,
this
,
num_uses
,
locations
.
size
());
for
(
Location
l
:
locations
)
l
.
dump
();
}
assembler
::
Immediate
RewriterVar
::
tryGetAsImmediate
(
bool
*
is_immediate
)
{
for
(
Location
l
:
locations
)
{
if
(
l
.
type
==
Location
::
Constant
)
{
*
is_immediate
=
true
;
return
assembler
::
Immediate
(
l
.
constant_val
);
}
}
*
is_immediate
=
false
;
return
assembler
::
Immediate
((
uint64_t
)
0
);
}
RewriterVar
Rewriter
::
getRsp
()
{
int
argnum
=
-
4
;
assembler
::
Register
RewriterVar
::
getInReg
(
Location
dest
)
{
assert
(
dest
.
type
==
Location
::
Register
||
dest
.
type
==
Location
::
AnyReg
);
// assembler::Register reg = var->rewriter->allocReg(l);
// var->rewriter->addLocationToVar(var, reg);
// return reg;
assert
(
locations
.
size
());
#ifndef NDEBUG
int
version
=
versions
[
argnum
];
#else
int
version
=
0
;
for
(
Location
l
:
locations
)
{
ASSERT
(
l
.
type
!=
Location
::
Constant
,
"why do you want this in a register?"
);
}
#endif
assert
(
version
);
return
RewriterVar
(
this
,
argnum
,
version
);
// Not sure if this is worth it,
// but first try to see if we're already in this specific register
for
(
Location
l
:
locations
)
{
if
(
l
==
dest
)
return
l
.
asRegister
();
}
// Then, see if we're in another register
for
(
Location
l
:
locations
)
{
if
(
l
.
type
==
Location
::
Register
)
{
assembler
::
Register
reg
=
l
.
asRegister
();
if
(
dest
.
type
!=
Location
::
AnyReg
)
{
assembler
::
Register
dest_reg
=
dest
.
asRegister
();
assert
(
dest_reg
!=
reg
);
// should have been caught by the previous case
rewriter
->
assembler
->
mov
(
reg
,
dest_reg
);
rewriter
->
addLocationToVar
(
this
,
dest_reg
);
return
dest_reg
;
}
return
reg
;
}
}
assert
(
locations
.
size
()
==
1
);
Location
l
(
*
locations
.
begin
());
assert
(
l
.
type
==
Location
::
Scratch
||
l
.
type
==
Location
::
Stack
);
assembler
::
Register
reg
=
rewriter
->
allocReg
(
dest
);
assert
(
rewriter
->
vars_by_location
.
count
(
reg
)
==
0
);
assembler
::
Indirect
mem
=
rewriter
->
indirectFor
(
l
);
rewriter
->
assembler
->
mov
(
mem
,
reg
);
rewriter
->
addLocationToVar
(
this
,
reg
);
return
reg
;
}
RewriterVar
Rewriter
::
getRbp
()
{
int
argnum
=
-
5
;
assembler
::
XMMRegister
RewriterVar
::
getInXMMReg
(
Location
dest
)
{
assert
(
dest
.
type
==
Location
::
XMMRegister
||
dest
.
type
==
Location
::
AnyReg
);
assert
(
locations
.
size
());
#ifndef NDEBUG
int
version
=
versions
[
argnum
];
#else
int
version
=
0
;
for
(
Location
l
:
locations
)
{
ASSERT
(
l
.
type
!=
Location
::
Constant
,
"why do you want this in a register?"
);
}
#endif
assert
(
version
);
return
RewriterVar
(
this
,
argnum
,
version
);
// Not sure if this is worth it,
// but first try to see if we're already in this specific register
for
(
Location
l
:
locations
)
{
if
(
l
==
dest
)
return
l
.
asXMMRegister
();
}
// Then, see if we're in another register
for
(
Location
l
:
locations
)
{
if
(
l
.
type
==
Location
::
XMMRegister
)
{
assembler
::
XMMRegister
reg
=
l
.
asXMMRegister
();
if
(
dest
.
type
!=
Location
::
AnyReg
)
{
assembler
::
XMMRegister
dest_reg
=
dest
.
asXMMRegister
();
assert
(
dest_reg
!=
reg
);
// should have been caught by the previous case
rewriter
->
assembler
->
movsd
(
reg
,
dest_reg
);
rewriter
->
addLocationToVar
(
this
,
dest_reg
);
return
dest_reg
;
}
return
reg
;
}
}
assert
(
locations
.
size
()
==
1
);
Location
l
(
*
locations
.
begin
());
assert
(
l
.
type
==
Location
::
Scratch
);
assert
(
dest
.
type
==
Location
::
XMMRegister
);
assembler
::
XMMRegister
reg
=
dest
.
asXMMRegister
();
assert
(
rewriter
->
vars_by_location
.
count
(
reg
)
==
0
);
assembler
::
Indirect
mem
=
rewriter
->
indirectFor
(
l
);
rewriter
->
assembler
->
movsd
(
mem
,
reg
);
rewriter
->
addLocationToVar
(
this
,
reg
);
return
reg
;
}
#ifndef NDEBUG
void
Rewriter
::
checkArgsValid
()
{
for
(
int
i
=
0
;
i
<
num_orig_args
;
i
++
)
checkVersion
(
i
,
i
+
7
);
RewriterVarUsage
::
RewriterVarUsage
()
:
var
(
NULL
),
done_using
(
true
)
{
}
int
Rewriter
::
mutate
(
int
argnum
)
{
ASSERT
(
locked
.
count
(
argnum
)
==
0
,
"arg %d is locked!"
,
argnum
);
assert
(
versions
.
count
(
argnum
));
RewriterVarUsage
RewriterVarUsage
::
empty
()
{
return
RewriterVarUsage
();
}
void
RewriterVar
::
decUse
()
{
num_uses
--
;
if
(
num_uses
==
0
)
{
rewriter
->
kill
(
this
);
delete
this
;
}
}
void
RewriterVar
::
incUse
()
{
num_uses
++
;
}
int
rtn_version
=
++
next_version
;
// printf("mutating %d to %d\n", argnum, rtn_version);
versions
[
argnum
]
=
rtn_version
;
return
rtn_version
;
bool
RewriterVar
::
isInLocation
(
Location
l
)
{
return
locations
.
count
(
l
)
!=
0
;
}
void
Rewriter
::
lock
(
int
argnum
)
{
assert
(
locked
.
count
(
argnum
)
==
0
);
locked
.
insert
(
argnum
);
void
Rewriter
::
setDoneGuarding
()
{
assert
(
!
done_guarding
);
done_guarding
=
true
;
for
(
RewriterVar
*
var
:
args
)
{
var
->
decUse
();
}
args
.
clear
();
}
void
Rewriter
::
unlock
(
int
argnum
)
{
assert
(
locked
.
count
(
argnum
)
==
1
);
locked
.
erase
(
argnum
);
RewriterVarUsage
Rewriter
::
getArg
(
int
argnum
)
{
assert
(
!
done_guarding
);
assert
(
argnum
>=
0
&&
argnum
<
args
.
size
());
RewriterVar
*
var
=
args
[
argnum
];
return
RewriterVarUsage
(
var
);
}
void
Rewriter
::
checkVersion
(
int
argnum
,
int
version
)
{
assert
(
version
>
0
);
ASSERT
(
version
==
versions
[
argnum
],
"arg %d got updated from %d to %d"
,
argnum
,
version
,
versions
[
argnum
]);
Location
Rewriter
::
getReturnDestination
()
{
return
return_location
;
}
#endif
void
Rewriter
::
trap
()
{
assembler
->
trap
();
}
void
Rewriter
::
nop
()
{
assembler
->
nop
();
RewriterVarUsage
Rewriter
::
loadConst
(
int64_t
val
,
Location
dest
)
{
if
(
val
>=
(
-
1L
<<
31
)
&&
val
<
(
1L
<<
31
)
-
1
)
{
Location
l
(
Location
::
Constant
,
val
);
RewriterVar
*&
var
=
vars_by_location
[
l
];
if
(
!
var
)
{
var
=
new
RewriterVar
(
this
,
l
);
}
return
RewriterVarUsage
(
var
);
}
assembler
::
Register
reg
=
allocReg
(
dest
);
RewriterVarUsage
var
=
createNewVar
(
reg
);
assembler
->
mov
(
assembler
::
Immediate
(
val
),
reg
);
// I guess you don't need std::move here:
return
var
;
}
void
Rewriter
::
annotate
(
int
num
)
{
assembler
->
emitAnnotation
(
num
);
RewriterVarUsage
Rewriter
::
call
(
bool
can_call_into_python
,
void
*
func_addr
,
RewriterVarUsage
arg0
)
{
std
::
vector
<
RewriterVarUsage
>
args
;
args
.
push_back
(
std
::
move
(
arg0
));
return
call
(
can_call_into_python
,
func_addr
,
std
::
move
(
args
));
}
RewriterVar
Rewriter
::
loadConst
(
int
argnum
,
intptr_t
val
)
{
assembler
->
mov
(
Immediate
(
val
),
fromArgnum
(
argnum
));
int
version
=
mutate
(
argnum
);
return
RewriterVar
(
this
,
argnum
,
version
);
RewriterVarUsage
Rewriter
::
call
(
bool
can_call_into_python
,
void
*
func_addr
,
RewriterVarUsage
arg0
,
RewriterVarUsage
arg1
)
{
std
::
vector
<
RewriterVarUsage
>
args
;
args
.
push_back
(
std
::
move
(
arg0
));
args
.
push_back
(
std
::
move
(
arg1
));
return
call
(
can_call_into_python
,
func_addr
,
std
::
move
(
args
));
}
RewriterVar
Rewriter
::
call
(
void
*
func_addr
)
{
static
const
Location
caller_save_registers
[]{
assembler
::
RAX
,
assembler
::
RCX
,
assembler
::
RDX
,
assembler
::
RSI
,
assembler
::
RDI
,
assembler
::
R8
,
assembler
::
R9
,
assembler
::
R10
,
assembler
::
R11
,
assembler
::
XMM0
,
assembler
::
XMM1
,
assembler
::
XMM2
,
assembler
::
XMM3
,
assembler
::
XMM4
,
assembler
::
XMM5
,
assembler
::
XMM6
,
assembler
::
XMM7
,
assembler
::
XMM8
,
assembler
::
XMM9
,
assembler
::
XMM10
,
assembler
::
XMM11
,
assembler
::
XMM12
,
assembler
::
XMM13
,
assembler
::
XMM14
,
assembler
::
XMM15
,
};
RewriterVarUsage
Rewriter
::
call
(
bool
can_call_into_python
,
void
*
func_addr
,
std
::
vector
<
RewriterVarUsage
>
args
)
{
// TODO figure out why this is here -- what needs to be done differently
// if can_call_into_python is true?
// assert(!can_call_into_python);
assertChangesOk
();
// RewriterVarUsage scratch = createNewVar(Location::any());
assembler
::
Register
r
=
allocReg
(
assembler
::
R11
);
for
(
int
i
=
0
;
i
<
args
.
size
();
i
++
)
{
Location
l
(
Location
::
forArg
(
i
));
RewriterVar
*
var
=
args
[
i
].
var
;
// printf("%d ", i);
// var->dump();
if
(
!
var
->
isInLocation
(
l
))
{
assembler
::
Register
r
=
l
.
asRegister
();
{
// this forces the register allocator to spill this register:
assembler
::
Register
r2
=
allocReg
(
l
);
assert
(
r
==
r2
);
assert
(
vars_by_location
.
count
(
l
)
==
0
);
}
// FIXME: get rid of tryGetAsImmediate
// instead do that work here; ex this could be a stack location
bool
is_immediate
;
assembler
::
Immediate
imm
=
var
->
tryGetAsImmediate
(
&
is_immediate
);
if
(
is_immediate
)
{
assembler
->
mov
(
imm
,
r
);
addLocationToVar
(
var
,
l
);
}
else
{
assembler
::
Register
r2
=
var
->
getInReg
(
l
);
assert
(
var
->
locations
.
count
(
r2
));
assert
(
r2
==
r
);
}
}
assert
(
var
->
isInLocation
(
Location
::
forArg
(
i
)));
}
#ifndef NDEBUG
changed_something
=
true
;
for
(
int
i
=
0
;
i
<
args
.
size
();
i
++
)
{
RewriterVar
*
var
=
args
[
i
].
var
;
if
(
!
var
->
isInLocation
(
Location
::
forArg
(
i
)))
{
var
->
dump
();
}
assert
(
var
->
isInLocation
(
Location
::
forArg
(
i
)));
}
#endif
// printf("%ld pushes, %d alloca bytes\n", pushes.size(), alloca_bytes);
int
bytes
=
8
*
pushes
.
size
()
+
alloca_bytes
;
bool
didpush
;
if
(
bytes
%
16
==
8
)
{
assembler
->
push
(
RDI
);
didpush
=
true
;
}
else
{
assert
(
bytes
%
16
==
0
);
didpush
=
false
;
// Spill caller-saved registers:
for
(
auto
check_reg
:
caller_save_registers
)
{
// check_reg.dump();
assert
(
check_reg
.
isClobberedByCall
());
auto
it
=
vars_by_location
.
find
(
check_reg
);
if
(
it
==
vars_by_location
.
end
())
continue
;
RewriterVar
*
var
=
it
->
second
;
bool
need_to_spill
=
true
;
for
(
Location
l
:
var
->
locations
)
{
if
(
!
l
.
isClobberedByCall
())
{
need_to_spill
=
false
;
break
;
}
}
for
(
int
i
=
0
;
i
<
args
.
size
();
i
++
)
{
if
(
args
[
i
].
var
==
var
)
{
if
(
var
->
num_uses
==
1
)
{
// If we hold the only usage of this arg var, we are
// going to kill all of its usages soon anyway,
// so we have no need to spill it.
need_to_spill
=
false
;
}
break
;
}
}
if
(
need_to_spill
)
{
if
(
check_reg
.
type
==
Location
::
Register
)
{
spillRegister
(
check_reg
.
asRegister
());
}
else
{
assert
(
check_reg
.
type
==
Location
::
XMMRegister
);
assert
(
var
->
locations
.
size
()
==
1
);
spillRegister
(
check_reg
.
asXMMRegister
());
}
}
else
{
removeLocationFromVar
(
var
,
check_reg
);
}
}
assembler
->
emitCall
(
func_addr
,
R11
);
// We call setDoneUsing after spilling because when we release these,
// we might release a pointer to an array in the scratch space allocated
// with _allocate. If we do that before spilling, we might spill into that
// scratch space.
for
(
int
i
=
0
;
i
<
args
.
size
();
i
++
)
{
args
[
i
].
setDoneUsing
();
}
if
(
didpush
)
assembler
->
pop
(
RDI
);
#ifndef NDEBUG
int
num_arg_regs
=
6
;
for
(
int
i
=
-
3
;
i
<
num_arg_regs
;
i
++
)
{
mutate
(
i
);
for
(
const
auto
&
p
:
vars_by_location
)
{
Location
l
=
p
.
first
;
// l.dump();
if
(
l
.
isClobberedByCall
())
{
p
.
second
->
dump
();
}
assert
(
!
l
.
isClobberedByCall
());
}
#endif
return
RewriterVar
(
this
,
-
1
,
mutate
(
-
1
));
assembler
->
mov
(
assembler
::
Immediate
(
func_addr
),
r
);
assembler
->
callq
(
r
);
assert
(
vars_by_location
.
count
(
assembler
::
RAX
)
==
0
);
RewriterVar
*
var
=
vars_by_location
[
assembler
::
RAX
]
=
new
RewriterVar
(
this
,
assembler
::
RAX
);
return
RewriterVarUsage
(
var
);
}
RewriterVar
Rewriter
::
pop
(
int
argnum
)
{
assert
(
pushes
.
size
()
>
0
);
void
Rewriter
::
commit
()
{
static
StatCounter
rewriter2_commits
(
"rewriter2_commits"
);
rewriter2_commits
.
log
();
assert
(
done_guarding
&&
"Could call setDoneGuarding for you, but probably best to do it yourself"
);
// if (!done_guarding)
// setDoneGuarding();
assert
(
live_out_regs
.
size
()
==
live_outs
.
size
());
for
(
int
i
=
0
;
i
<
live_outs
.
size
();
i
++
)
{
assembler
::
GenericRegister
ru
=
assembler
::
GenericRegister
::
fromDwarf
(
live_out_regs
[
i
]);
Location
expected
(
ru
);
RewriterVar
*
var
=
live_outs
[
i
];
// for (Location l : var->locations) {
// printf("%d %d\n", l.type, l._data);
//}
if
(
!
var
->
isInLocation
(
expected
))
{
assert
(
vars_by_location
.
count
(
expected
)
==
0
);
if
(
ru
.
type
==
assembler
::
GenericRegister
::
GP
)
{
assembler
::
Register
reg
=
var
->
getInReg
(
ru
.
gp
);
assert
(
reg
==
ru
.
gp
);
}
else
if
(
ru
.
type
==
assembler
::
GenericRegister
::
XMM
)
{
assembler
::
XMMRegister
reg
=
var
->
getInXMMReg
(
ru
.
xmm
);
assert
(
reg
==
ru
.
xmm
);
}
else
{
RELEASE_ASSERT
(
0
,
"%d"
,
ru
.
type
);
}
}
int
version
=
pushes
.
back
();
pushes
.
pop_back
();
#ifndef NDEBUG
versions
[
argnum
]
=
version
;
#endif
// printf("popping %d to %d\n", version, argnum);
assert
(
var
->
isInLocation
(
ru
));
var
->
decUse
();
}
assembler
->
pop
(
fromArgnum
(
argnum
));
return
RewriterVar
(
this
,
argnum
,
version
);
assert
(
vars_by_location
.
size
()
==
0
);
rewrite
->
commit
(
decision_path
,
this
);
}
void
Rewriter
::
finishAssembly
(
int
continue_offset
)
{
assembler
->
jmp
(
assembler
::
JumpDestination
::
fromStart
(
continue_offset
));
assembler
->
fillWithNops
();
}
void
Rewriter
::
commitReturning
(
RewriterVarUsage
usage
)
{
// assert(usage.var->isInLocation(getReturnDestination()));
usage
.
var
->
getInReg
(
getReturnDestination
());
usage
.
setDoneUsing
();
commit
();
}
void
Rewriter
::
addDecision
(
int
way
)
{
...
...
@@ -445,23 +700,294 @@ void Rewriter::addDependenceOn(ICInvalidator& invalidator) {
rewrite
->
addDependenceOn
(
invalidator
);
}
void
Rewriter
::
commit
()
{
static
StatCounter
rewriter_commits
(
"rewriter_commits"
);
rewriter_commits
.
log
();
void
Rewriter
::
kill
(
RewriterVar
*
var
)
{
for
(
RewriterVarUsage
&
scratch_range_usage
:
var
->
scratch_range
)
{
// Should be the only usage for this particular var (we
// hold the only usage) so it should cause the array to
// be deallocated.
scratch_range_usage
.
setDoneUsing
();
}
var
->
scratch_range
.
clear
();
// make sure we left the stack the way we found it:
assert
(
pushes
.
size
()
==
0
);
assert
(
alloca_bytes
==
0
);
for
(
Location
l
:
var
->
locations
)
{
assert
(
vars_by_location
[
l
]
==
var
);
vars_by_location
.
erase
(
l
);
}
}
rewrite
->
commit
(
decision_path
,
this
);
Location
Rewriter
::
allocScratch
()
{
int
scratch_bytes
=
rewrite
->
getScratchBytes
();
for
(
int
i
=
0
;
i
<
scratch_bytes
;
i
+=
8
)
{
Location
l
(
Location
::
Scratch
,
i
);
if
(
vars_by_location
.
count
(
l
)
==
0
)
{
return
l
;
}
}
RELEASE_ASSERT
(
0
,
"Using all %d bytes of scratch!"
,
scratch_bytes
);
}
std
::
pair
<
RewriterVarUsage
,
int
>
Rewriter
::
_allocate
(
int
n
)
{
assert
(
n
>=
1
);
int
scratch_bytes
=
rewrite
->
getScratchBytes
();
int
consec
=
0
;
for
(
int
i
=
0
;
i
<
scratch_bytes
;
i
+=
8
)
{
Location
l
(
Location
::
Scratch
,
i
);
if
(
vars_by_location
.
count
(
l
)
==
0
)
{
consec
++
;
if
(
consec
==
n
)
{
int
a
=
i
/
8
-
n
+
1
;
int
b
=
i
/
8
;
assembler
::
Register
r
=
allocReg
(
Location
::
any
());
// TODO should be a LEA instruction
// In fact, we could do something like we do for constants and only load
// this when necessary, so it won't spill. Is that worth?
assembler
->
mov
(
assembler
::
RBP
,
r
);
assembler
->
add
(
assembler
::
Immediate
(
8
*
a
+
rewrite
->
getScratchRbpOffset
()),
r
);
RewriterVarUsage
usage
=
createNewVar
(
r
);
for
(
int
j
=
a
;
j
<=
b
;
j
++
)
{
Location
m
(
Location
::
Scratch
,
j
*
8
);
RewriterVarUsage
placeholder
=
createNewVar
(
m
);
usage
.
var
->
scratch_range
.
push_back
(
std
::
move
(
placeholder
));
}
return
std
::
make_pair
(
std
::
move
(
usage
),
a
);
}
}
else
{
consec
=
0
;
}
}
RELEASE_ASSERT
(
0
,
"Using all %d bytes of scratch!"
,
scratch_bytes
);
}
void
Rewriter
::
finishAssembly
(
int
continue_offset
)
{
assembler
->
jmp
(
JumpDestination
::
fromStart
(
continue_offset
));
RewriterVarUsage
Rewriter
::
allocate
(
int
n
)
{
return
_allocate
(
n
).
first
;
}
RewriterVarUsage
Rewriter
::
allocateAndCopy
(
RewriterVarUsage
array_ptr
,
int
n
)
{
// TODO smart register allocation
array_ptr
.
assertValid
();
std
::
pair
<
RewriterVarUsage
,
int
>
allocation
=
_allocate
(
n
);
int
offset
=
allocation
.
second
;
assembler
::
Register
tmp
=
allocReg
(
Location
::
any
());
assembler
::
Register
src_ptr
=
array_ptr
.
var
->
getInReg
();
assert
(
tmp
!=
src_ptr
);
// TODO how to ensure this?
for
(
int
i
=
0
;
i
<
n
;
i
++
)
{
assembler
->
mov
(
assembler
::
Indirect
(
src_ptr
,
8
*
i
),
tmp
);
assembler
->
mov
(
tmp
,
assembler
::
Indirect
(
assembler
::
RBP
,
8
*
(
offset
+
i
)
+
rewrite
->
getScratchRbpOffset
()));
}
array_ptr
.
setDoneUsing
();
return
std
::
move
(
allocation
.
first
);
}
RewriterVarUsage
Rewriter
::
allocateAndCopyPlus1
(
RewriterVarUsage
first_elem
,
RewriterVarUsage
rest_ptr
,
int
n_rest
)
{
first_elem
.
assertValid
();
if
(
n_rest
>
0
)
rest_ptr
.
assertValid
();
else
assert
(
rest_ptr
.
isDoneUsing
());
std
::
pair
<
RewriterVarUsage
,
int
>
allocation
=
_allocate
(
n_rest
+
1
);
int
offset
=
allocation
.
second
;
assembler
::
Register
tmp
=
first_elem
.
var
->
getInReg
();
assembler
->
mov
(
tmp
,
assembler
::
Indirect
(
assembler
::
RBP
,
8
*
offset
+
rewrite
->
getScratchRbpOffset
()));
if
(
n_rest
>
0
)
{
assembler
::
Register
src_ptr
=
rest_ptr
.
var
->
getInReg
();
// TODO if this triggers we'll need a way to allocate two distinct registers
assert
(
tmp
!=
src_ptr
);
for
(
int
i
=
0
;
i
<
n_rest
;
i
++
)
{
assembler
->
mov
(
assembler
::
Indirect
(
src_ptr
,
8
*
i
),
tmp
);
assembler
->
mov
(
tmp
,
assembler
::
Indirect
(
assembler
::
RBP
,
8
*
(
offset
+
i
+
1
)
+
rewrite
->
getScratchRbpOffset
()));
}
rest_ptr
.
setDoneUsing
();
}
first_elem
.
setDoneUsing
();
assembler
->
fillWithNopsExcept
(
max_pushes
);
for
(
int
i
=
0
;
i
<
max_pushes
;
i
++
)
{
assembler
->
pop
(
RAX
);
return
std
::
move
(
allocation
.
first
);
}
assembler
::
Indirect
Rewriter
::
indirectFor
(
Location
l
)
{
assert
(
l
.
type
==
Location
::
Scratch
||
l
.
type
==
Location
::
Stack
);
if
(
l
.
type
==
Location
::
Scratch
)
// TODO it can sometimes be more efficient to do RSP-relative addressing?
return
assembler
::
Indirect
(
assembler
::
RBP
,
rewrite
->
getScratchRbpOffset
()
+
l
.
scratch_offset
);
else
return
assembler
::
Indirect
(
assembler
::
RSP
,
l
.
stack_offset
);
}
void
Rewriter
::
spillRegister
(
assembler
::
Register
reg
)
{
// Don't spill a register than an input argument is in, unless
// we are done guarding (in which case `args` will be empty)
for
(
int
i
=
0
;
i
<
args
.
size
();
i
++
)
{
assert
(
!
args
[
i
]
->
isInLocation
(
Location
(
reg
)));
}
RewriterVar
*
var
=
vars_by_location
[
reg
];
assert
(
var
);
// First, try to spill into a callee-save register:
for
(
assembler
::
Register
new_reg
:
allocatable_regs
)
{
if
(
!
new_reg
.
isCalleeSave
())
continue
;
if
(
vars_by_location
.
count
(
new_reg
))
continue
;
assembler
->
mov
(
reg
,
new_reg
);
addLocationToVar
(
var
,
new_reg
);
removeLocationFromVar
(
var
,
reg
);
return
;
}
Location
scratch
=
allocScratch
();
assembler
::
Indirect
mem
=
indirectFor
(
scratch
);
assembler
->
mov
(
reg
,
mem
);
addLocationToVar
(
var
,
scratch
);
removeLocationFromVar
(
var
,
reg
);
}
void
Rewriter
::
spillRegister
(
assembler
::
XMMRegister
reg
)
{
assert
(
done_guarding
);
RewriterVar
*
var
=
vars_by_location
[
reg
];
assert
(
var
);
assert
(
var
->
locations
.
size
()
==
1
);
Location
scratch
=
allocScratch
();
assembler
::
Indirect
mem
=
indirectFor
(
scratch
);
assembler
->
movsd
(
reg
,
mem
);
addLocationToVar
(
var
,
scratch
);
removeLocationFromVar
(
var
,
reg
);
}
assembler
::
Register
Rewriter
::
allocReg
(
Location
dest
)
{
if
(
dest
.
type
==
Location
::
AnyReg
)
{
for
(
assembler
::
Register
reg
:
allocatable_regs
)
{
if
(
vars_by_location
.
count
(
reg
)
==
0
)
return
reg
;
}
return
allocReg
(
assembler
::
R15
);
// seem as fine as any
}
else
if
(
dest
.
type
==
Location
::
Register
)
{
assembler
::
Register
reg
(
dest
.
regnum
);
if
(
vars_by_location
.
count
(
reg
))
{
spillRegister
(
reg
);
}
assert
(
vars_by_location
.
count
(
reg
)
==
0
);
return
reg
;
}
else
{
RELEASE_ASSERT
(
0
,
"%d"
,
dest
.
type
);
}
}
void
Rewriter
::
addLocationToVar
(
RewriterVar
*
var
,
Location
l
)
{
assert
(
!
var
->
isInLocation
(
l
));
assert
(
vars_by_location
.
count
(
l
)
==
0
);
ASSERT
(
l
.
type
==
Location
::
Register
||
l
.
type
==
Location
::
XMMRegister
||
l
.
type
==
Location
::
Scratch
,
"%d"
,
l
.
type
);
var
->
locations
.
insert
(
l
);
vars_by_location
[
l
]
=
var
;
}
void
Rewriter
::
removeLocationFromVar
(
RewriterVar
*
var
,
Location
l
)
{
assert
(
var
->
isInLocation
(
l
));
assert
(
vars_by_location
[
l
]
==
var
);
vars_by_location
.
erase
(
l
);
var
->
locations
.
erase
(
l
);
}
RewriterVarUsage
Rewriter
::
createNewVar
(
Location
dest
)
{
RewriterVar
*&
var
=
vars_by_location
[
dest
];
assert
(
!
var
);
var
=
new
RewriterVar
(
this
,
dest
);
return
var
;
}
TypeRecorder
*
Rewriter
::
getTypeRecorder
()
{
return
rewrite
->
getTypeRecorder
();
}
Rewriter
::
Rewriter
(
ICSlotRewrite
*
rewrite
,
int
num_args
,
const
std
::
vector
<
int
>&
live_outs
)
:
rewrite
(
rewrite
),
assembler
(
rewrite
->
getAssembler
()),
return_location
(
rewrite
->
returnRegister
()),
done_guarding
(
false
),
ndecisions
(
0
),
decision_path
(
1
)
{
// assembler->trap();
for
(
int
i
=
0
;
i
<
num_args
;
i
++
)
{
Location
l
=
Location
::
forArg
(
i
);
RewriterVar
*
var
=
new
RewriterVar
(
this
,
l
);
vars_by_location
[
l
]
=
var
;
var
->
incUse
();
args
.
push_back
(
var
);
}
static
StatCounter
rewriter_starts
(
"rewriter2_starts"
);
rewriter_starts
.
log
();
static
StatCounter
rewriter_spillsavoided
(
"rewriter2_spillsavoided"
);
// Calculate the list of live-ins based off the live-outs list,
// and create a Use of them so that they get preserved
for
(
int
dwarf_regnum
:
live_outs
)
{
assembler
::
GenericRegister
ru
=
assembler
::
GenericRegister
::
fromDwarf
(
dwarf_regnum
);
Location
l
(
ru
);
// We could handle this here, but for now we're assuming that the return destination
// will get removed from this list before it gets handed to us.
assert
(
l
!=
getReturnDestination
());
//// The return register is the only live-out that is not also a live-in.
// if (l == getReturnDestination()) {
// l.dump();
// continue;
//}
if
(
l
.
isClobberedByCall
())
{
rewriter_spillsavoided
.
log
();
}
RewriterVar
*&
var
=
vars_by_location
[
l
];
if
(
!
var
)
{
var
=
new
RewriterVar
(
this
,
l
);
}
var
->
incUse
();
this
->
live_outs
.
push_back
(
var
);
this
->
live_out_regs
.
push_back
(
dwarf_regnum
);
}
}
Rewriter
*
Rewriter
::
createRewriter
(
void
*
rtn_addr
,
int
num_args
,
const
char
*
debug_name
)
{
ICInfo
*
ic
=
getICInfo
(
rtn_addr
);
static
StatCounter
rewriter_nopatch
(
"rewriter_nopatch"
);
if
(
!
ic
)
{
rewriter_nopatch
.
log
();
return
NULL
;
}
return
new
Rewriter
(
ic
->
startRewrite
(
debug_name
),
num_args
,
ic
->
getLiveOuts
());
}
RewriterVarUsage
RewriterVarUsage
::
addUse
()
{
return
RewriterVarUsage
(
var
);
}
}
src/asm_writing/rewriter.h
View file @
50eb5bed
...
...
@@ -16,130 +16,275 @@
#define PYSTON_ASMWRITING_REWRITER_H
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include "asm_writing/assembler.h"
#include "asm_writing/icinfo.h"
#include "core/ast.h"
namespace
pyston
{
class
TypeRecorder
;
class
ICInfo
;
class
ICSlotInfo
;
class
ICSlotRewrite
;
class
ICInvalidator
;
class
Rewriter
;
namespace
assembler
{
class
Assembler
;
class
RewriterVar
;
struct
Location
{
public:
enum
LocationType
:
uint8_t
{
Register
,
XMMRegister
,
Stack
,
Scratch
,
// stack location, relative to the scratch start
// For representing constants that fit in 32-bits, that can be encoded as immediates
Constant
,
AnyReg
,
// special type for use when specifying a location as a destination
None
,
// special type that represents the lack of a location, ex where a "ret void" gets returned
Uninitialized
,
// special type for an uninitialized (and invalid) location
};
public:
LocationType
type
;
union
{
// only valid if type==Register; uses X86 numbering, not dwarf numbering.
// also valid if type==XMMRegister
int32_t
regnum
;
// only valid if type==Stack; this is the offset from bottom of the original frame.
// ie argument #6 will have a stack_offset of 0, #7 will have a stack offset of 8, etc
int32_t
stack_offset
;
// only valid if type == Scratch; offset from the beginning of the scratch area
int32_t
scratch_offset
;
// only valid if type==Constant
int32_t
constant_val
;
int32_t
_data
;
};
constexpr
Location
()
:
type
(
Uninitialized
),
_data
(
-
1
)
{}
constexpr
Location
(
const
Location
&
r
)
:
type
(
r
.
type
),
_data
(
r
.
_data
)
{}
Location
operator
=
(
const
Location
&
r
)
{
type
=
r
.
type
;
_data
=
r
.
_data
;
return
*
this
;
}
constexpr
Location
(
LocationType
type
,
int32_t
data
)
:
type
(
type
),
_data
(
data
)
{}
constexpr
Location
(
assembler
::
Register
reg
)
:
type
(
Register
),
regnum
(
reg
.
regnum
)
{}
constexpr
Location
(
assembler
::
XMMRegister
reg
)
:
type
(
XMMRegister
),
regnum
(
reg
.
regnum
)
{}
constexpr
Location
(
assembler
::
GenericRegister
reg
)
:
type
(
reg
.
type
==
assembler
::
GenericRegister
::
GP
?
Register
:
reg
.
type
==
assembler
::
GenericRegister
::
XMM
?
XMMRegister
:
None
),
regnum
(
reg
.
type
==
assembler
::
GenericRegister
::
GP
?
reg
.
gp
.
regnum
:
reg
.
xmm
.
regnum
)
{}
assembler
::
Register
asRegister
()
const
;
assembler
::
XMMRegister
asXMMRegister
()
const
;
bool
isClobberedByCall
()
const
;
static
constexpr
Location
any
()
{
return
Location
(
AnyReg
,
0
);
}
static
constexpr
Location
none
()
{
return
Location
(
None
,
0
);
}
static
Location
forArg
(
int
argnum
);
bool
operator
==
(
const
Location
rhs
)
const
{
return
this
->
asInt
()
==
rhs
.
asInt
();
}
bool
operator
!=
(
const
Location
rhs
)
const
{
return
!
(
*
this
==
rhs
);
}
uint64_t
asInt
()
const
{
return
(
int
)
type
+
((
uint64_t
)
_data
<<
4
);
}
void
dump
()
const
;
};
static_assert
(
sizeof
(
Location
)
<=
8
,
""
);
}
// TODO Maybe should make these classes abstract and then pick between a debug implementation
// and a release one, instead of trying to make one class do both?
namespace
std
{
template
<
>
struct
hash
<
pyston
::
Location
>
{
size_t
operator
()(
const
pyston
::
Location
p
)
const
{
return
p
.
asInt
();
}
};
}
namespace
pyston
{
class
RewriterVarUsage
{
public:
enum
KillFlag
{
NoKill
,
Kill
,
};
class
RewriterVar
{
private:
Rewriter
*
rewriter
;
int
argnum
;
int
version
;
RewriterVar
*
var
;
bool
done_using
;
RewriterVarUsage
();
RewriterVarUsage
(
const
RewriterVarUsage
&
)
=
delete
;
RewriterVarUsage
&
operator
=
(
const
RewriterVarUsage
&
)
=
delete
;
void
assertValid
()
{
assert
(
var
);
assert
(
!
done_using
);
}
public:
RewriterVar
()
:
rewriter
(
NULL
),
argnum
(
-
100
),
version
(
-
100
)
{}
RewriterVar
(
Rewriter
*
rewriter
,
int
argnum
,
int
version
);
RewriterVar
&
operator
=
(
const
RewriterVar
&
rhs
);
// Creates a new Usage object of this var; ownership of
// one use of the var gets passed to this new object.
RewriterVarUsage
(
RewriterVar
*
var
);
// Move constructor; don't need it for performance reasons, but because
// semantically we have to pass the ownership of the use.
RewriterVarUsage
(
RewriterVarUsage
&&
usage
);
RewriterVarUsage
&
operator
=
(
RewriterVarUsage
&&
usage
);
static
RewriterVarUsage
empty
();
#ifndef NDEBUG
void
assertValid
()
const
;
void
lock
();
void
unlock
();
#else
inline
void
assertValid
()
const
{}
inline
void
lock
()
{}
inline
void
unlock
()
{}
~
RewriterVarUsage
()
{
if
(
!
std
::
uncaught_exception
())
assert
(
done_using
);
}
#endif
int
getArgnum
();
void
addGuard
(
intptr_t
val
);
void
addGuardNotEq
(
intptr_t
val
);
// More efficient than getAttr().addGuard(), but less efficient than addGuard() if the value is already available:
void
addAttrGuard
(
int
offset
,
intptr_t
val
);
RewriterVar
getAttr
(
int
offset
,
int
dest
);
void
incAttr
(
int
offset
);
void
setAttr
(
int
offset
,
const
RewriterVar
&
val
,
bool
user_visible
=
true
);
RewriterVar
move
(
int
argnum
);
bool
isInReg
();
void
push
();
RewriterVar
cmp
(
AST_TYPE
::
AST_TYPE
cmp_type
,
const
RewriterVar
&
val
,
int
dest
);
RewriterVar
toBool
(
int
dest
);
RewriterVar
add
(
int64_t
amount
);
void
setDoneUsing
();
bool
isDoneUsing
();
void
ensureDoneUsing
();
RewriterVarUsage
addUse
();
void
addGuard
(
uint64_t
val
);
void
addGuardNotEq
(
uint64_t
val
);
void
addAttrGuard
(
int
offset
,
uint64_t
val
);
RewriterVarUsage
getAttr
(
int
offset
,
KillFlag
kill
,
Location
loc
=
Location
::
any
());
void
setAttr
(
int
offset
,
RewriterVarUsage
other
);
RewriterVarUsage
cmp
(
AST_TYPE
::
AST_TYPE
cmp_type
,
RewriterVarUsage
other
,
Location
loc
=
Location
::
any
());
RewriterVarUsage
toBool
(
KillFlag
kill
,
Location
loc
=
Location
::
any
());
friend
class
Rewriter
;
};
class
Rewriter
;
// This might make more sense as an inner class of Rewriter, but
// you can't forward-declare that :/
class
RewriterVar
{
private:
Rewriter
*
rewriter
;
int
num_uses
;
std
::
unordered_set
<
Location
>
locations
;
bool
isInLocation
(
Location
l
);
// Indicates that this value is a pointer to a fixed-size range in the scratch space.
// This is a vector of variable usages that keep the range allocated.
std
::
vector
<
RewriterVarUsage
>
scratch_range
;
// Gets a copy of this variable in a register, spilling/reloading if necessary.
// TODO have to be careful with the result since the interface doesn't guarantee
// that the register will still contain your value when you go to use it
assembler
::
Register
getInReg
(
Location
l
=
Location
::
any
());
assembler
::
XMMRegister
getInXMMReg
(
Location
l
=
Location
::
any
());
// If this is an immediate, try getting it as one
assembler
::
Immediate
tryGetAsImmediate
(
bool
*
is_immediate
);
void
dump
();
public:
void
incUse
();
void
decUse
();
RewriterVar
(
Rewriter
*
rewriter
,
Location
location
)
:
rewriter
(
rewriter
),
num_uses
(
0
)
{
assert
(
rewriter
);
locations
.
insert
(
location
);
}
friend
class
RewriterVarUsage
;
friend
class
Rewriter
;
};
class
Rewriter
:
public
ICSlotRewrite
::
CommitHook
{
private:
std
::
unique_ptr
<
ICSlotRewrite
>
rewrite
;
assembler
::
Assembler
*
assembler
;
const
int
num_orig_args
;
const
int
num_temp_regs
;
void
finishAssembly
(
int
continue_offset
)
;
const
Location
return_location
;
int
alloca_bytes
;
int
max_pushes
;
std
::
vector
<
int
>
pushes
;
#ifndef NDEBUG
std
::
unordered_map
<
int
,
int
>
versions
;
int
next_version
;
bool
changed_something
;
std
::
unordered_set
<
int
>
locked
;
#endif
int
ndecisions
;
uint64_t
decision_path
;
bool
done_guarding
;
Rewriter
(
ICSlotRewrite
*
rewrite
,
int
num_orig_args
,
int
num_temp_regs
)
;
std
::
vector
<
int
>
live_out_regs
;
void
addPush
(
int
version
);
std
::
unordered_map
<
Location
,
RewriterVar
*>
vars_by_location
;
std
::
vector
<
RewriterVar
*>
args
;
std
::
vector
<
RewriterVar
*>
live_outs
;
public:
static
Rewriter
*
createRewriter
(
void
*
ic_rtn_addr
,
int
num_orig_args
,
int
num_temp_regs
,
const
char
*
debug_name
);
Rewriter
(
ICSlotRewrite
*
rewrite
,
int
num_args
,
const
std
::
vector
<
int
>&
live_outs
);
#ifndef NDEBUG
int
mutate
(
int
argnum
);
void
lock
(
int
argnum
);
void
unlock
(
int
argnum
);
void
checkVersion
(
int
argnum
,
int
version
);
void
checkArgsValid
();
#else
inline
int
mutate
(
int
argnum
)
{
return
0
;
}
inline
void
lock
(
int
argnum
)
{}
inline
void
unlock
(
int
argnum
)
{}
inline
void
checkVersion
(
int
argnum
,
int
version
)
{}
inline
void
checkArgsValid
()
{}
#endif
void
assertChangesOk
()
{
assert
(
done_guarding
);
}
int
getFuncStackSize
()
{
return
rewrite
->
getFuncStackSize
();
}
int
getScratchRbpOffset
()
{
return
rewrite
->
getScratchRbpOffset
();
}
int
getScratchBytes
()
{
return
rewrite
->
getScratchBytes
();
}
RewriterVar
getRbp
();
RewriterVar
getRsp
();
void
kill
(
RewriterVar
*
var
);
void
addDecision
(
int
way
);
// Allocates a register. dest must be of type Register or AnyReg
assembler
::
Register
allocReg
(
Location
dest
);
// Allocates an 8-byte region in the scratch space
Location
allocScratch
();
assembler
::
Indirect
indirectFor
(
Location
l
);
// Spills a specified register.
// If there are open callee-save registers, takes one of those, otherwise goes on the stack
void
spillRegister
(
assembler
::
Register
reg
);
// Similar, but for XMM registers (always go on the stack)
void
spillRegister
(
assembler
::
XMMRegister
reg
);
// Given an empty location, do the internal bookkeeping to create a new var out of that location.
RewriterVarUsage
createNewVar
(
Location
dest
);
// Do the bookkeeping to say that var is now also in location l
void
addLocationToVar
(
RewriterVar
*
var
,
Location
l
);
// Do the bookkeeping to say that var is no longer in location l
void
removeLocationFromVar
(
RewriterVar
*
var
,
Location
l
);
void
finishAssembly
(
int
continue_offset
)
override
;
RewriterVar
alloca_
(
int
bytes
,
int
dest_argnum
);
RewriterVar
getArg
(
int
argnum
);
std
::
pair
<
RewriterVarUsage
,
int
>
_allocate
(
int
n
);
int
ndecisions
;
uint64_t
decision_path
;
public:
// This should be called exactly once for each argument
RewriterVarUsage
getArg
(
int
argnum
);
Location
getReturnDestination
();
bool
isDoneGuarding
()
{
return
done_guarding
;
}
void
setDoneGuarding
();
TypeRecorder
*
getTypeRecorder
();
void
trap
();
void
nop
();
void
annotate
(
int
num
);
RewriterVar
pop
(
int
argnum
);
RewriterVar
call
(
void
*
func_addr
);
RewriterVar
loadConst
(
int
argnum
,
intptr_t
val
);
RewriterVarUsage
loadConst
(
int64_t
val
,
Location
loc
=
Location
::
any
());
RewriterVarUsage
call
(
bool
can_call_into_python
,
void
*
func_addr
,
std
::
vector
<
RewriterVarUsage
>
args
);
RewriterVarUsage
call
(
bool
can_call_into_python
,
void
*
func_addr
,
RewriterVarUsage
arg0
);
RewriterVarUsage
call
(
bool
can_call_into_python
,
void
*
func_addr
,
RewriterVarUsage
arg0
,
RewriterVarUsage
arg1
);
RewriterVarUsage
allocate
(
int
n
);
RewriterVarUsage
allocateAndCopy
(
RewriterVarUsage
array
,
int
n
);
RewriterVarUsage
allocateAndCopyPlus1
(
RewriterVarUsage
first_elem
,
RewriterVarUsage
rest
,
int
n_rest
);
void
deallocateStack
(
int
nbytes
);
void
addDependenceOn
(
ICInvalidator
&
);
void
commit
();
void
commitReturning
(
RewriterVarUsage
rtn
);
void
addDependenceOn
(
ICInvalidator
&
);
static
Rewriter
*
createRewriter
(
void
*
rtn_addr
,
int
num_args
,
const
char
*
debug_name
);
void
addDecision
(
int
way
);
friend
class
RewriterVar
;
friend
class
RewriterVarUsage
;
};
}
...
...
src/asm_writing/rewriter2.cpp
deleted
100644 → 0
View file @
d0863da3
// Copyright (c) 2014 Dropbox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "asm_writing/rewriter2.h"
#include <vector>
#include "asm_writing/icinfo.h"
#include "core/common.h"
#include "core/stats.h"
namespace
pyston
{
static
const
assembler
::
Register
allocatable_regs
[]
=
{
assembler
::
RAX
,
assembler
::
RCX
,
assembler
::
RBX
,
assembler
::
RDX
,
// no RSP
// no RBP
assembler
::
RDI
,
assembler
::
RSI
,
assembler
::
R8
,
assembler
::
R9
,
assembler
::
R10
,
assembler
::
R11
,
assembler
::
R12
,
assembler
::
R13
,
assembler
::
R14
,
assembler
::
R15
,
};
Location
Location
::
forArg
(
int
argnum
)
{
assert
(
argnum
>=
0
);
switch
(
argnum
)
{
case
0
:
return
assembler
::
RDI
;
case
1
:
return
assembler
::
RSI
;
case
2
:
return
assembler
::
RDX
;
case
3
:
return
assembler
::
RCX
;
case
4
:
return
assembler
::
R8
;
case
5
:
return
assembler
::
R9
;
default:
break
;
}
RELEASE_ASSERT
(
0
,
"the following is untested"
);
// int offset = (argnum - 6) * 8;
// return Location(Stack, offset);
}
assembler
::
Register
Location
::
asRegister
()
const
{
assert
(
type
==
Register
);
return
assembler
::
Register
(
regnum
);
}
assembler
::
XMMRegister
Location
::
asXMMRegister
()
const
{
assert
(
type
==
XMMRegister
);
return
assembler
::
XMMRegister
(
regnum
);
}
bool
Location
::
isClobberedByCall
()
const
{
if
(
type
==
Register
)
{
return
!
asRegister
().
isCalleeSave
();
}
if
(
type
==
XMMRegister
)
return
true
;
if
(
type
==
Scratch
)
return
false
;
if
(
type
==
Constant
)
return
false
;
RELEASE_ASSERT
(
0
,
"%d"
,
type
);
}
void
Location
::
dump
()
const
{
if
(
type
==
Register
)
{
asRegister
().
dump
();
return
;
}
if
(
type
==
XMMRegister
)
{
printf
(
"%%xmm%d
\n
"
,
regnum
);
return
;
}
if
(
type
==
Scratch
)
{
printf
(
"scratch(%d)
\n
"
,
stack_offset
);
return
;
}
if
(
type
==
Constant
)
{
printf
(
"imm(%d)
\n
"
,
constant_val
);
return
;
}
RELEASE_ASSERT
(
0
,
"%d"
,
type
);
}
RewriterVarUsage2
::
RewriterVarUsage2
(
RewriterVar2
*
var
)
:
var
(
var
),
done_using
(
false
)
{
assert
(
var
->
rewriter
);
}
void
RewriterVarUsage2
::
addAttrGuard
(
int
offset
,
uint64_t
val
)
{
Rewriter2
*
rewriter
=
var
->
rewriter
;
assembler
::
Assembler
*
assembler
=
rewriter
->
assembler
;
assert
(
!
rewriter
->
done_guarding
&&
"too late to add a guard!"
);
assertValid
();
assembler
::
Register
this_reg
=
var
->
getInReg
();
if
(
val
<
(
-
1L
<<
31
)
||
val
>=
(
1L
<<
31
)
-
1
)
{
assembler
::
Register
reg
=
rewriter
->
allocReg
(
Location
::
any
());
assembler
->
mov
(
assembler
::
Immediate
(
val
),
reg
);
assembler
->
cmp
(
assembler
::
Indirect
(
this_reg
,
offset
),
reg
);
}
else
{
assembler
->
cmp
(
assembler
::
Indirect
(
this_reg
,
offset
),
assembler
::
Immediate
(
val
));
}
assembler
->
jne
(
assembler
::
JumpDestination
::
fromStart
(
rewriter
->
rewrite
->
getSlotSize
()));
}
RewriterVarUsage2
RewriterVarUsage2
::
getAttr
(
int
offset
,
KillFlag
kill
,
Location
dest
)
{
assertValid
();
// Save these, since if we kill this register the var might disappear:
assembler
::
Register
this_reg
=
var
->
getInReg
();
Rewriter2
*
rewriter
=
var
->
rewriter
;
if
(
kill
==
Kill
)
{
setDoneUsing
();
}
assembler
::
Register
newvar_reg
=
rewriter
->
allocReg
(
dest
);
RewriterVarUsage2
newvar
=
rewriter
->
createNewVar
(
newvar_reg
);
rewriter
->
assembler
->
mov
(
assembler
::
Indirect
(
this_reg
,
offset
),
newvar_reg
);
return
std
::
move
(
newvar
);
}
void
RewriterVarUsage2
::
setAttr
(
int
offset
,
RewriterVarUsage2
val
)
{
assertValid
();
var
->
rewriter
->
assertChangesOk
();
assembler
::
Register
this_reg
=
var
->
getInReg
();
bool
is_immediate
;
assembler
::
Immediate
imm
=
val
.
var
->
tryGetAsImmediate
(
&
is_immediate
);
if
(
is_immediate
)
{
var
->
rewriter
->
assembler
->
movq
(
imm
,
assembler
::
Indirect
(
this_reg
,
offset
));
}
else
{
assembler
::
Register
other_reg
=
val
.
var
->
getInReg
();
// TODO the allocator could choose to spill this_reg in order to load other_reg...
// Hopefuly it won't make that decision, so we should just be able to guard on it for now:
assert
(
this_reg
!=
other_reg
);
var
->
rewriter
->
assembler
->
mov
(
other_reg
,
assembler
::
Indirect
(
this_reg
,
offset
));
}
val
.
setDoneUsing
();
}
void
RewriterVarUsage2
::
setDoneUsing
()
{
assertValid
();
done_using
=
true
;
var
->
decUse
();
var
=
NULL
;
}
RewriterVarUsage2
::
RewriterVarUsage2
(
RewriterVarUsage2
&&
usage
)
{
assert
(
!
usage
.
done_using
);
assert
(
usage
.
var
!=
NULL
);
var
=
usage
.
var
;
done_using
=
usage
.
done_using
;
usage
.
var
=
NULL
;
usage
.
done_using
=
true
;
}
RewriterVarUsage2
&
RewriterVarUsage2
::
operator
=
(
RewriterVarUsage2
&&
usage
)
{
assert
(
done_using
);
assert
(
var
==
NULL
);
assert
(
!
usage
.
done_using
);
assert
(
usage
.
var
!=
NULL
);
var
=
usage
.
var
;
done_using
=
usage
.
done_using
;
usage
.
var
=
NULL
;
usage
.
done_using
=
true
;
return
*
this
;
}
void
RewriterVar2
::
dump
()
{
printf
(
"RewriterVar2 at %p: %d uses. %ld locations:
\n
"
,
this
,
num_uses
,
locations
.
size
());
for
(
Location
l
:
locations
)
l
.
dump
();
}
assembler
::
Immediate
RewriterVar2
::
tryGetAsImmediate
(
bool
*
is_immediate
)
{
for
(
Location
l
:
locations
)
{
if
(
l
.
type
==
Location
::
Constant
)
{
*
is_immediate
=
true
;
return
assembler
::
Immediate
(
l
.
constant_val
);
}
}
*
is_immediate
=
false
;
return
assembler
::
Immediate
((
uint64_t
)
0
);
}
assembler
::
Register
RewriterVar2
::
getInReg
(
Location
dest
)
{
assert
(
dest
.
type
==
Location
::
Register
||
dest
.
type
==
Location
::
AnyReg
);
// assembler::Register reg = var->rewriter->allocReg(l);
// var->rewriter->addLocationToVar(var, reg);
// return reg;
assert
(
locations
.
size
());
#ifndef NDEBUG
for
(
Location
l
:
locations
)
{
ASSERT
(
l
.
type
!=
Location
::
Constant
,
"why do you want this in a register?"
);
}
#endif
// Not sure if this is worth it,
// but first try to see if we're already in this specific register
for
(
Location
l
:
locations
)
{
if
(
l
==
dest
)
return
l
.
asRegister
();
}
// Then, see if we're in another register
for
(
Location
l
:
locations
)
{
if
(
l
.
type
==
Location
::
Register
)
{
assembler
::
Register
reg
=
l
.
asRegister
();
if
(
dest
.
type
!=
Location
::
AnyReg
)
{
assembler
::
Register
dest_reg
=
dest
.
asRegister
();
assert
(
dest_reg
!=
reg
);
// should have been caught by the previous case
rewriter
->
assembler
->
mov
(
reg
,
dest_reg
);
rewriter
->
addLocationToVar
(
this
,
dest_reg
);
return
dest_reg
;
}
return
reg
;
}
}
assert
(
locations
.
size
()
==
1
);
Location
l
(
*
locations
.
begin
());
assert
(
l
.
type
==
Location
::
Scratch
);
assembler
::
Register
reg
=
rewriter
->
allocReg
(
dest
);
assert
(
rewriter
->
vars_by_location
.
count
(
reg
)
==
0
);
assembler
::
Indirect
mem
=
rewriter
->
indirectFor
(
l
);
rewriter
->
assembler
->
mov
(
mem
,
reg
);
rewriter
->
addLocationToVar
(
this
,
reg
);
return
reg
;
}
assembler
::
XMMRegister
RewriterVar2
::
getInXMMReg
(
Location
dest
)
{
assert
(
dest
.
type
==
Location
::
XMMRegister
||
dest
.
type
==
Location
::
AnyReg
);
assert
(
locations
.
size
());
#ifndef NDEBUG
for
(
Location
l
:
locations
)
{
ASSERT
(
l
.
type
!=
Location
::
Constant
,
"why do you want this in a register?"
);
}
#endif
// Not sure if this is worth it,
// but first try to see if we're already in this specific register
for
(
Location
l
:
locations
)
{
if
(
l
==
dest
)
return
l
.
asXMMRegister
();
}
// Then, see if we're in another register
for
(
Location
l
:
locations
)
{
if
(
l
.
type
==
Location
::
XMMRegister
)
{
assembler
::
XMMRegister
reg
=
l
.
asXMMRegister
();
if
(
dest
.
type
!=
Location
::
AnyReg
)
{
assembler
::
XMMRegister
dest_reg
=
dest
.
asXMMRegister
();
assert
(
dest_reg
!=
reg
);
// should have been caught by the previous case
rewriter
->
assembler
->
movsd
(
reg
,
dest_reg
);
rewriter
->
addLocationToVar
(
this
,
dest_reg
);
return
dest_reg
;
}
return
reg
;
}
}
assert
(
locations
.
size
()
==
1
);
Location
l
(
*
locations
.
begin
());
assert
(
l
.
type
==
Location
::
Scratch
);
assert
(
dest
.
type
==
Location
::
XMMRegister
);
assembler
::
XMMRegister
reg
=
dest
.
asXMMRegister
();
assert
(
rewriter
->
vars_by_location
.
count
(
reg
)
==
0
);
assembler
::
Indirect
mem
=
rewriter
->
indirectFor
(
l
);
rewriter
->
assembler
->
movsd
(
mem
,
reg
);
rewriter
->
addLocationToVar
(
this
,
reg
);
return
reg
;
}
RewriterVarUsage2
::
RewriterVarUsage2
()
:
var
(
NULL
),
done_using
(
true
)
{
}
RewriterVarUsage2
RewriterVarUsage2
::
empty
()
{
return
RewriterVarUsage2
();
}
void
RewriterVar2
::
decUse
()
{
num_uses
--
;
if
(
num_uses
==
0
)
{
rewriter
->
kill
(
this
);
delete
this
;
}
}
void
RewriterVar2
::
incUse
()
{
num_uses
++
;
}
bool
RewriterVar2
::
isInLocation
(
Location
l
)
{
return
locations
.
count
(
l
)
!=
0
;
}
void
Rewriter2
::
setDoneGuarding
()
{
assert
(
!
done_guarding
);
done_guarding
=
true
;
for
(
RewriterVar2
*
var
:
args
)
{
var
->
decUse
();
}
args
.
clear
();
}
RewriterVarUsage2
Rewriter2
::
getArg
(
int
argnum
)
{
assert
(
!
done_guarding
);
assert
(
argnum
>=
0
&&
argnum
<
args
.
size
());
RewriterVar2
*
var
=
args
[
argnum
];
var
->
incUse
();
return
RewriterVarUsage2
(
var
);
}
Location
Rewriter2
::
getReturnDestination
()
{
return
return_location
;
}
void
Rewriter2
::
trap
()
{
assembler
->
trap
();
}
RewriterVarUsage2
Rewriter2
::
loadConst
(
int64_t
val
,
Location
dest
)
{
if
(
val
>=
(
-
1L
<<
31
)
&&
val
<
(
1L
<<
31
)
-
1
)
{
Location
l
(
Location
::
Constant
,
val
);
return
createNewVar
(
l
);
}
assembler
::
Register
reg
=
allocReg
(
dest
);
RewriterVarUsage2
var
=
createNewVar
(
reg
);
assembler
->
mov
(
assembler
::
Immediate
(
val
),
reg
);
// I guess you don't need std::move here:
return
var
;
}
RewriterVarUsage2
Rewriter2
::
call
(
bool
can_call_into_python
,
void
*
func_addr
,
RewriterVarUsage2
arg0
)
{
std
::
vector
<
RewriterVarUsage2
>
args
;
args
.
push_back
(
std
::
move
(
arg0
));
return
call
(
can_call_into_python
,
func_addr
,
std
::
move
(
args
));
}
RewriterVarUsage2
Rewriter2
::
call
(
bool
can_call_into_python
,
void
*
func_addr
,
RewriterVarUsage2
arg0
,
RewriterVarUsage2
arg1
)
{
std
::
vector
<
RewriterVarUsage2
>
args
;
args
.
push_back
(
std
::
move
(
arg0
));
args
.
push_back
(
std
::
move
(
arg1
));
return
call
(
can_call_into_python
,
func_addr
,
std
::
move
(
args
));
}
static
const
Location
caller_save_registers
[]{
assembler
::
RAX
,
assembler
::
RCX
,
assembler
::
RDX
,
assembler
::
RSI
,
assembler
::
RDI
,
assembler
::
R8
,
assembler
::
R9
,
assembler
::
R10
,
assembler
::
R11
,
assembler
::
XMM0
,
assembler
::
XMM1
,
assembler
::
XMM2
,
assembler
::
XMM3
,
assembler
::
XMM4
,
assembler
::
XMM5
,
assembler
::
XMM6
,
assembler
::
XMM7
,
assembler
::
XMM8
,
assembler
::
XMM9
,
assembler
::
XMM10
,
assembler
::
XMM11
,
assembler
::
XMM12
,
assembler
::
XMM13
,
assembler
::
XMM14
,
assembler
::
XMM15
,
};
RewriterVarUsage2
Rewriter2
::
call
(
bool
can_call_into_python
,
void
*
func_addr
,
std
::
vector
<
RewriterVarUsage2
>
args
)
{
assert
(
!
can_call_into_python
);
assertChangesOk
();
// RewriterVarUsage2 scratch = createNewVar(Location::any());
assembler
::
Register
r
=
allocReg
(
assembler
::
R11
);
for
(
int
i
=
0
;
i
<
args
.
size
();
i
++
)
{
Location
l
(
Location
::
forArg
(
i
));
RewriterVar2
*
var
=
args
[
i
].
var
;
// printf("%d ", i);
// var->dump();
if
(
!
var
->
isInLocation
(
l
))
{
assembler
::
Register
r
=
l
.
asRegister
();
{
// this forces the register allocator to spill this register:
assembler
::
Register
r2
=
allocReg
(
l
);
assert
(
r
==
r2
);
assert
(
vars_by_location
.
count
(
l
)
==
0
);
}
// FIXME: get rid of tryGetAsImmediate
// instead do that work here; ex this could be a stack location
bool
is_immediate
;
assembler
::
Immediate
imm
=
var
->
tryGetAsImmediate
(
&
is_immediate
);
if
(
is_immediate
)
{
assembler
->
mov
(
imm
,
r
);
addLocationToVar
(
var
,
l
);
}
else
{
assembler
::
Register
r2
=
var
->
getInReg
(
l
);
assert
(
var
->
locations
.
count
(
r2
));
assert
(
r2
==
r
);
}
}
assert
(
var
->
isInLocation
(
Location
::
forArg
(
i
)));
}
#ifndef NDEBUG
for
(
int
i
=
0
;
i
<
args
.
size
();
i
++
)
{
RewriterVar2
*
var
=
args
[
i
].
var
;
if
(
!
var
->
isInLocation
(
Location
::
forArg
(
i
)))
{
var
->
dump
();
}
assert
(
var
->
isInLocation
(
Location
::
forArg
(
i
)));
}
#endif
// This is kind of hacky: we release the use of these right now,
// and then expect that everything else will not clobber any of the arguments.
// Naively moving this below the reg spilling will always spill the arguments;
// but sometimes you need to do that if the argument lives past the call.
// Hacky, but the right way to do it requires a bit of reworking so that it can
// spill but keep its current use.
for
(
int
i
=
0
;
i
<
args
.
size
();
i
++
)
{
args
[
i
].
setDoneUsing
();
}
// Spill caller-saved registers:
for
(
auto
check_reg
:
caller_save_registers
)
{
// check_reg.dump();
assert
(
check_reg
.
isClobberedByCall
());
auto
it
=
vars_by_location
.
find
(
check_reg
);
if
(
it
==
vars_by_location
.
end
())
continue
;
RewriterVar2
*
var
=
it
->
second
;
bool
need_to_spill
=
true
;
for
(
Location
l
:
var
->
locations
)
{
if
(
!
l
.
isClobberedByCall
())
{
need_to_spill
=
false
;
break
;
}
}
if
(
need_to_spill
)
{
if
(
check_reg
.
type
==
Location
::
Register
)
{
spillRegister
(
check_reg
.
asRegister
());
}
else
{
assert
(
check_reg
.
type
==
Location
::
XMMRegister
);
assert
(
var
->
locations
.
size
()
==
1
);
spillRegister
(
check_reg
.
asXMMRegister
());
}
}
else
{
removeLocationFromVar
(
var
,
check_reg
);
}
}
#ifndef NDEBUG
for
(
const
auto
&
p
:
vars_by_location
)
{
Location
l
=
p
.
first
;
// l.dump();
if
(
l
.
isClobberedByCall
())
{
p
.
second
->
dump
();
}
assert
(
!
l
.
isClobberedByCall
());
}
#endif
assembler
->
mov
(
assembler
::
Immediate
(
func_addr
),
r
);
assembler
->
callq
(
r
);
assert
(
vars_by_location
.
count
(
assembler
::
RAX
)
==
0
);
RewriterVar2
*
var
=
vars_by_location
[
assembler
::
RAX
]
=
new
RewriterVar2
(
this
,
assembler
::
RAX
);
return
RewriterVarUsage2
(
var
);
}
void
Rewriter2
::
commit
()
{
static
StatCounter
rewriter2_commits
(
"rewriter2_commits"
);
rewriter2_commits
.
log
();
assert
(
done_guarding
&&
"Could call setDoneGuarding for you, but probably best to do it yourself"
);
// if (!done_guarding)
// setDoneGuarding();
assert
(
live_out_regs
.
size
()
==
live_outs
.
size
());
for
(
int
i
=
0
;
i
<
live_outs
.
size
();
i
++
)
{
assembler
::
GenericRegister
ru
=
assembler
::
GenericRegister
::
fromDwarf
(
live_out_regs
[
i
]);
Location
expected
(
ru
);
RewriterVar2
*
var
=
live_outs
[
i
];
// for (Location l : var->locations) {
// printf("%d %d\n", l.type, l._data);
//}
if
(
!
var
->
isInLocation
(
expected
))
{
assert
(
vars_by_location
.
count
(
expected
)
==
0
);
if
(
ru
.
type
==
assembler
::
GenericRegister
::
GP
)
{
assembler
::
Register
reg
=
var
->
getInReg
(
ru
.
gp
);
assert
(
reg
==
ru
.
gp
);
}
else
if
(
ru
.
type
==
assembler
::
GenericRegister
::
XMM
)
{
assembler
::
XMMRegister
reg
=
var
->
getInXMMReg
(
ru
.
xmm
);
assert
(
reg
==
ru
.
xmm
);
}
else
{
RELEASE_ASSERT
(
0
,
"%d"
,
ru
.
type
);
}
}
assert
(
var
->
isInLocation
(
ru
));
var
->
decUse
();
}
assert
(
vars_by_location
.
size
()
==
0
);
rewrite
->
commit
(
0
,
this
);
}
void
Rewriter2
::
finishAssembly
(
int
continue_offset
)
{
assembler
->
jmp
(
assembler
::
JumpDestination
::
fromStart
(
continue_offset
));
assembler
->
fillWithNops
();
}
void
Rewriter2
::
commitReturning
(
RewriterVarUsage2
usage
)
{
assert
(
usage
.
var
->
isInLocation
(
getReturnDestination
()));
/*
Location l = usage.var->location;
Location expected = getReturnDestination();
if (l != expected) {
assert(l.type == Location::Register);
assert(expected.type == Location::Register);
assembler->mov(l.asRegister(), expected.asRegister());
}
*/
usage
.
setDoneUsing
();
commit
();
}
void
Rewriter2
::
addDependenceOn
(
ICInvalidator
&
invalidator
)
{
rewrite
->
addDependenceOn
(
invalidator
);
}
void
Rewriter2
::
kill
(
RewriterVar2
*
var
)
{
for
(
Location
l
:
var
->
locations
)
{
assert
(
vars_by_location
[
l
]
==
var
);
vars_by_location
.
erase
(
l
);
}
}
Location
Rewriter2
::
allocScratch
()
{
int
scratch_bytes
=
rewrite
->
getScratchBytes
();
for
(
int
i
=
0
;
i
<
scratch_bytes
;
i
+=
8
)
{
Location
l
(
Location
::
Scratch
,
i
);
if
(
vars_by_location
.
count
(
l
)
==
0
)
return
l
;
}
RELEASE_ASSERT
(
0
,
"Using all %d bytes of scratch!"
,
scratch_bytes
);
}
assembler
::
Indirect
Rewriter2
::
indirectFor
(
Location
l
)
{
assert
(
l
.
type
==
Location
::
Scratch
);
// TODO it can sometimes be more efficient to do RSP-relative addressing?
int
rbp_offset
=
rewrite
->
getScratchRbpOffset
()
+
l
.
scratch_offset
;
return
assembler
::
Indirect
(
assembler
::
RBP
,
rbp_offset
);
}
void
Rewriter2
::
spillRegister
(
assembler
::
Register
reg
)
{
assert
(
done_guarding
);
RewriterVar2
*
var
=
vars_by_location
[
reg
];
assert
(
var
);
// First, try to spill into a callee-save register:
for
(
assembler
::
Register
new_reg
:
allocatable_regs
)
{
if
(
!
new_reg
.
isCalleeSave
())
continue
;
if
(
vars_by_location
.
count
(
new_reg
))
continue
;
assembler
->
mov
(
reg
,
new_reg
);
addLocationToVar
(
var
,
new_reg
);
removeLocationFromVar
(
var
,
reg
);
return
;
}
Location
scratch
=
allocScratch
();
assembler
::
Indirect
mem
=
indirectFor
(
scratch
);
assembler
->
mov
(
reg
,
mem
);
addLocationToVar
(
var
,
scratch
);
removeLocationFromVar
(
var
,
reg
);
}
void
Rewriter2
::
spillRegister
(
assembler
::
XMMRegister
reg
)
{
assert
(
done_guarding
);
RewriterVar2
*
var
=
vars_by_location
[
reg
];
assert
(
var
);
assert
(
var
->
locations
.
size
()
==
1
);
Location
scratch
=
allocScratch
();
assembler
::
Indirect
mem
=
indirectFor
(
scratch
);
assembler
->
movsd
(
reg
,
mem
);
addLocationToVar
(
var
,
scratch
);
removeLocationFromVar
(
var
,
reg
);
}
assembler
::
Register
Rewriter2
::
allocReg
(
Location
dest
)
{
if
(
dest
.
type
==
Location
::
AnyReg
)
{
for
(
assembler
::
Register
reg
:
allocatable_regs
)
{
if
(
vars_by_location
.
count
(
reg
)
==
0
)
return
reg
;
}
RELEASE_ASSERT
(
0
,
"couldn't find a reg to allocate and haven't added spilling"
);
}
else
if
(
dest
.
type
==
Location
::
Register
)
{
assembler
::
Register
reg
(
dest
.
regnum
);
if
(
vars_by_location
.
count
(
reg
))
{
spillRegister
(
reg
);
}
assert
(
vars_by_location
.
count
(
reg
)
==
0
);
return
reg
;
}
else
{
RELEASE_ASSERT
(
0
,
"%d"
,
dest
.
type
);
}
}
void
Rewriter2
::
addLocationToVar
(
RewriterVar2
*
var
,
Location
l
)
{
assert
(
!
var
->
isInLocation
(
l
));
assert
(
vars_by_location
.
count
(
l
)
==
0
);
ASSERT
(
l
.
type
==
Location
::
Register
||
l
.
type
==
Location
::
XMMRegister
||
l
.
type
==
Location
::
Scratch
,
"%d"
,
l
.
type
);
var
->
locations
.
insert
(
l
);
vars_by_location
[
l
]
=
var
;
}
void
Rewriter2
::
removeLocationFromVar
(
RewriterVar2
*
var
,
Location
l
)
{
assert
(
var
->
isInLocation
(
l
));
assert
(
vars_by_location
[
l
]
=
var
);
vars_by_location
.
erase
(
l
);
var
->
locations
.
erase
(
l
);
}
RewriterVarUsage2
Rewriter2
::
createNewVar
(
Location
dest
)
{
RewriterVar2
*&
var
=
vars_by_location
[
dest
];
assert
(
!
var
);
var
=
new
RewriterVar2
(
this
,
dest
);
return
var
;
}
TypeRecorder
*
Rewriter2
::
getTypeRecorder
()
{
return
rewrite
->
getTypeRecorder
();
}
Rewriter2
::
Rewriter2
(
ICSlotRewrite
*
rewrite
,
int
num_args
,
const
std
::
vector
<
int
>&
live_outs
)
:
rewrite
(
rewrite
),
assembler
(
rewrite
->
getAssembler
()),
return_location
(
rewrite
->
returnRegister
()),
done_guarding
(
false
)
{
// assembler->trap();
for
(
int
i
=
0
;
i
<
num_args
;
i
++
)
{
Location
l
=
Location
::
forArg
(
i
);
RewriterVar2
*
var
=
new
RewriterVar2
(
this
,
l
);
vars_by_location
[
l
]
=
var
;
args
.
push_back
(
var
);
}
static
StatCounter
rewriter_starts
(
"rewriter2_starts"
);
rewriter_starts
.
log
();
static
StatCounter
rewriter_spillsavoided
(
"rewriter2_spillsavoided"
);
// Calculate the list of live-ins based off the live-outs list,
// and create a Use of them so that they get preserved
for
(
int
dwarf_regnum
:
live_outs
)
{
assembler
::
GenericRegister
ru
=
assembler
::
GenericRegister
::
fromDwarf
(
dwarf_regnum
);
Location
l
(
ru
);
// We could handle this here, but for now we're assuming that the return destination
// will get removed from this list before it gets handed to us.
assert
(
l
!=
getReturnDestination
());
//// The return register is the only live-out that is not also a live-in.
// if (l == getReturnDestination()) {
// l.dump();
// continue;
//}
if
(
l
.
isClobberedByCall
())
{
rewriter_spillsavoided
.
log
();
}
RewriterVar2
*&
var
=
vars_by_location
[
l
];
if
(
var
)
{
var
->
incUse
();
}
else
{
var
=
new
RewriterVar2
(
this
,
l
);
}
this
->
live_outs
.
push_back
(
var
);
this
->
live_out_regs
.
push_back
(
dwarf_regnum
);
}
}
Rewriter2
*
Rewriter2
::
createRewriter
(
void
*
rtn_addr
,
int
num_args
,
const
char
*
debug_name
)
{
ICInfo
*
ic
=
getICInfo
(
rtn_addr
);
static
StatCounter
rewriter_nopatch
(
"rewriter_nopatch"
);
if
(
!
ic
)
{
rewriter_nopatch
.
log
();
return
NULL
;
}
return
new
Rewriter2
(
ic
->
startRewrite
(
debug_name
),
num_args
,
ic
->
getLiveOuts
());
}
}
src/asm_writing/rewriter2.h
deleted
100644 → 0
View file @
d0863da3
// Copyright (c) 2014 Dropbox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef PYSTON_ASMWRITING_REWRITER2_H
#define PYSTON_ASMWRITING_REWRITER2_H
#include <memory>
#include "asm_writing/assembler.h"
#include "asm_writing/icinfo.h"
namespace
pyston
{
class
TypeRecorder
;
class
ICInfo
;
class
ICSlotInfo
;
class
ICSlotRewrite
;
class
ICInvalidator
;
class
RewriterVar2
;
struct
Location
{
public:
enum
LocationType
:
uint8_t
{
Register
,
XMMRegister
,
// Stack,
Scratch
,
// stack location, relative to the scratch start
// For representing constants that fit in 32-bits, that can be encoded as immediates
Constant
,
AnyReg
,
// special type for use when specifying a location as a destination
None
,
// special type that represents the lack of a location, ex where a "ret void" gets returned
Uninitialized
,
// special type for an uninitialized (and invalid) location
};
public:
LocationType
type
;
union
{
// only valid if type==Register; uses X86 numbering, not dwarf numbering.
// also valid if type==XMMRegister
int32_t
regnum
;
// only valid if type==Stack; this is the offset from bottom of the original frame.
// ie argument #6 will have a stack_offset of 0, #7 will have a stack offset of 8, etc
int32_t
stack_offset
;
// only valid if type == Scratch; offset from the beginning of the scratch area
int32_t
scratch_offset
;
// only valid if type==Constant
int32_t
constant_val
;
int32_t
_data
;
};
constexpr
Location
()
:
type
(
Uninitialized
),
_data
(
-
1
)
{}
constexpr
Location
(
const
Location
&
r
)
:
type
(
r
.
type
),
_data
(
r
.
_data
)
{}
Location
operator
=
(
const
Location
&
r
)
{
type
=
r
.
type
;
_data
=
r
.
_data
;
return
*
this
;
}
constexpr
Location
(
LocationType
type
,
int32_t
data
)
:
type
(
type
),
_data
(
data
)
{}
constexpr
Location
(
assembler
::
Register
reg
)
:
type
(
Register
),
regnum
(
reg
.
regnum
)
{}
constexpr
Location
(
assembler
::
XMMRegister
reg
)
:
type
(
XMMRegister
),
regnum
(
reg
.
regnum
)
{}
constexpr
Location
(
assembler
::
GenericRegister
reg
)
:
type
(
reg
.
type
==
assembler
::
GenericRegister
::
GP
?
Register
:
reg
.
type
==
assembler
::
GenericRegister
::
XMM
?
XMMRegister
:
None
),
regnum
(
reg
.
type
==
assembler
::
GenericRegister
::
GP
?
reg
.
gp
.
regnum
:
reg
.
xmm
.
regnum
)
{}
assembler
::
Register
asRegister
()
const
;
assembler
::
XMMRegister
asXMMRegister
()
const
;
bool
isClobberedByCall
()
const
;
static
constexpr
Location
any
()
{
return
Location
(
AnyReg
,
0
);
}
static
constexpr
Location
none
()
{
return
Location
(
None
,
0
);
}
static
Location
forArg
(
int
argnum
);
bool
operator
==
(
const
Location
rhs
)
const
{
return
this
->
asInt
()
==
rhs
.
asInt
();
}
bool
operator
!=
(
const
Location
rhs
)
const
{
return
!
(
*
this
==
rhs
);
}
uint64_t
asInt
()
const
{
return
(
int
)
type
+
((
uint64_t
)
_data
<<
4
);
}
void
dump
()
const
;
};
static_assert
(
sizeof
(
Location
)
<=
8
,
""
);
}
namespace
std
{
template
<
>
struct
hash
<
pyston
::
Location
>
{
size_t
operator
()(
const
pyston
::
Location
p
)
const
{
return
p
.
asInt
();
}
};
}
namespace
pyston
{
class
RewriterVarUsage2
{
public:
enum
KillFlag
{
NoKill
,
Kill
,
};
private:
RewriterVar2
*
var
;
bool
done_using
;
RewriterVarUsage2
();
RewriterVarUsage2
(
const
RewriterVarUsage2
&
)
=
delete
;
RewriterVarUsage2
&
operator
=
(
const
RewriterVarUsage2
&
)
=
delete
;
void
assertValid
()
{
assert
(
var
);
assert
(
!
done_using
);
}
public:
// Creates a new Usage object of this var; ownership of
// one use of the var gets passed to this new object.
RewriterVarUsage2
(
RewriterVar2
*
var
);
// Move constructor; don't need it for performance reasons, but because
// semantically we have to pass the ownership of the use.
RewriterVarUsage2
(
RewriterVarUsage2
&&
usage
);
RewriterVarUsage2
&
operator
=
(
RewriterVarUsage2
&&
usage
);
static
RewriterVarUsage2
empty
();
#ifndef NDEBUG
~
RewriterVarUsage2
()
{
if
(
!
std
::
uncaught_exception
())
assert
(
done_using
);
}
#endif
void
setDoneUsing
();
// RewriterVarUsage2 addUse() { return var->addUse(); }
RewriterVarUsage2
addUse
();
void
addAttrGuard
(
int
offset
,
uint64_t
val
);
RewriterVarUsage2
getAttr
(
int
offset
,
KillFlag
kill
,
Location
loc
=
Location
::
any
());
void
setAttr
(
int
offset
,
RewriterVarUsage2
other
);
friend
class
Rewriter2
;
};
class
Rewriter2
;
// This might make more sense as an inner class of Rewriter2, but
// you can't forward-declare that :/
class
RewriterVar2
{
private:
Rewriter2
*
rewriter
;
int
num_uses
;
std
::
unordered_set
<
Location
>
locations
;
bool
isInLocation
(
Location
l
);
// Gets a copy of this variable in a register, spilling/reloading if necessary.
// TODO have to be careful with the result since the interface doesn't guarantee
// that the register will still contain your value when you go to use it
assembler
::
Register
getInReg
(
Location
l
=
Location
::
any
());
assembler
::
XMMRegister
getInXMMReg
(
Location
l
=
Location
::
any
());
// If this is an immediate, try getting it as one
assembler
::
Immediate
tryGetAsImmediate
(
bool
*
is_immediate
);
void
dump
();
public:
void
incUse
();
void
decUse
();
RewriterVar2
(
Rewriter2
*
rewriter
,
Location
location
)
:
rewriter
(
rewriter
),
num_uses
(
1
)
{
assert
(
rewriter
);
locations
.
insert
(
location
);
}
friend
class
RewriterVarUsage2
;
friend
class
Rewriter2
;
};
class
Rewriter2
:
public
ICSlotRewrite
::
CommitHook
{
private:
std
::
unique_ptr
<
ICSlotRewrite
>
rewrite
;
assembler
::
Assembler
*
assembler
;
const
Location
return_location
;
bool
done_guarding
;
std
::
vector
<
int
>
live_out_regs
;
std
::
unordered_map
<
Location
,
RewriterVar2
*>
vars_by_location
;
std
::
vector
<
RewriterVar2
*>
args
;
std
::
vector
<
RewriterVar2
*>
live_outs
;
Rewriter2
(
ICSlotRewrite
*
rewrite
,
int
num_args
,
const
std
::
vector
<
int
>&
live_outs
);
void
assertChangesOk
()
{
assert
(
done_guarding
);
}
void
kill
(
RewriterVar2
*
var
);
// Allocates a register. dest must be of type Register or AnyReg
assembler
::
Register
allocReg
(
Location
dest
);
// Allocates an 8-byte region in the scratch space
Location
allocScratch
();
assembler
::
Indirect
indirectFor
(
Location
l
);
// Spills a specified register.
// If there are open callee-save registers, takes one of those, otherwise goes on the stack
void
spillRegister
(
assembler
::
Register
reg
);
// Similar, but for XMM registers (always go on the stack)
void
spillRegister
(
assembler
::
XMMRegister
reg
);
// Given an empty location, do the internal bookkeeping to create a new var out of that location.
RewriterVarUsage2
createNewVar
(
Location
dest
);
// Do the bookkeeping to say that var is now also in location l
void
addLocationToVar
(
RewriterVar2
*
var
,
Location
l
);
// Do the bookkeeping to say that var is no longer in location l
void
removeLocationFromVar
(
RewriterVar2
*
var
,
Location
l
);
void
finishAssembly
(
int
continue_offset
)
override
;
public:
// This should be called exactly once for each argument
RewriterVarUsage2
getArg
(
int
argnum
);
Location
getReturnDestination
();
bool
isDoneGuarding
()
{
return
done_guarding
;
}
void
setDoneGuarding
();
TypeRecorder
*
getTypeRecorder
();
void
trap
();
RewriterVarUsage2
loadConst
(
int64_t
val
,
Location
loc
=
Location
::
any
());
RewriterVarUsage2
call
(
bool
can_call_into_python
,
void
*
func_addr
,
std
::
vector
<
RewriterVarUsage2
>
args
);
RewriterVarUsage2
call
(
bool
can_call_into_python
,
void
*
func_addr
,
RewriterVarUsage2
arg0
);
RewriterVarUsage2
call
(
bool
can_call_into_python
,
void
*
func_addr
,
RewriterVarUsage2
arg0
,
RewriterVarUsage2
arg1
);
void
commit
();
void
commitReturning
(
RewriterVarUsage2
rtn
);
void
addDependenceOn
(
ICInvalidator
&
);
static
Rewriter2
*
createRewriter
(
void
*
rtn_addr
,
int
num_args
,
const
char
*
debug_name
);
friend
class
RewriterVar2
;
friend
class
RewriterVarUsage2
;
};
}
#endif
src/core/cfg.cpp
View file @
50eb5bed
...
...
@@ -1812,7 +1812,7 @@ CFG* computeCFG(SourceInfo* source, std::vector<AST_stmt*> body) {
if
(
source
->
ast
->
type
==
AST_TYPE
::
ClassDef
)
{
// A classdef always starts with "__module__ = __name__"
Box
*
module_name
=
source
->
parent_module
->
getattr
(
"__name__"
,
NULL
,
NULL
);
Box
*
module_name
=
source
->
parent_module
->
getattr
(
"__name__"
,
NULL
);
assert
(
module_name
->
cls
==
str_cls
);
AST_Assign
*
module_assign
=
new
AST_Assign
();
module_assign
->
targets
.
push_back
(
makeName
(
"__module__"
,
AST_TYPE
::
Store
));
...
...
src/core/types.h
View file @
50eb5bed
...
...
@@ -54,6 +54,11 @@ struct ArgPassSpec {
int
totalPassed
()
{
return
num_args
+
num_keywords
+
(
has_starargs
?
1
:
0
)
+
(
has_kwargs
?
1
:
0
);
}
uintptr_t
asInt
()
const
{
return
*
reinterpret_cast
<
const
uintptr_t
*>
(
this
);
}
void
dump
()
{
printf
(
"(has_starargs=%s, has_kwargs=%s, num_keywords=%d, num_args=%d)
\n
"
,
has_starargs
?
"true"
:
"false"
,
has_kwargs
?
"true"
:
"false"
,
num_keywords
,
num_args
);
}
};
static_assert
(
sizeof
(
ArgPassSpec
)
<=
sizeof
(
void
*
),
"ArgPassSpec doesn't fit in register!"
);
...
...
@@ -371,10 +376,9 @@ private:
};
class
SetattrRewriteArgs
2
;
class
SetattrRewriteArgs
;
class
GetattrRewriteArgs
;
class
GetattrRewriteArgs2
;
class
DelattrRewriteArgs2
;
class
DelattrRewriteArgs
;
struct
HCAttrs
{
public:
...
...
@@ -398,15 +402,15 @@ public:
HCAttrs
*
getAttrsPtr
();
void
setattr
(
const
std
::
string
&
attr
,
Box
*
val
,
SetattrRewriteArgs
2
*
rewrite_args2
);
void
setattr
(
const
std
::
string
&
attr
,
Box
*
val
,
SetattrRewriteArgs
*
rewrite_args
);
void
giveAttr
(
const
std
::
string
&
attr
,
Box
*
val
)
{
assert
(
this
->
getattr
(
attr
)
==
NULL
);
this
->
setattr
(
attr
,
val
,
NULL
);
}
Box
*
getattr
(
const
std
::
string
&
attr
,
GetattrRewriteArgs
*
rewrite_args
,
GetattrRewriteArgs2
*
rewrite_args2
);
Box
*
getattr
(
const
std
::
string
&
attr
)
{
return
getattr
(
attr
,
NULL
,
NULL
);
}
void
delattr
(
const
std
::
string
&
attr
,
DelattrRewriteArgs
2
*
rewrite_args
);
Box
*
getattr
(
const
std
::
string
&
attr
,
GetattrRewriteArgs
*
rewrite_args
);
Box
*
getattr
(
const
std
::
string
&
attr
)
{
return
getattr
(
attr
,
NULL
);
}
void
delattr
(
const
std
::
string
&
attr
,
DelattrRewriteArgs
*
rewrite_args
);
};
...
...
src/runtime/builtin_modules/builtins.cpp
View file @
50eb5bed
...
...
@@ -56,7 +56,7 @@ extern "C" Box* dir(Box* obj) {
}
// If __dict__ is present use its keys and add the reset below
Box
*
obj_dict
=
getattr_internal
(
obj
,
"__dict__"
,
false
,
true
,
nullptr
,
nullptr
);
Box
*
obj_dict
=
getattr_internal
(
obj
,
"__dict__"
,
false
,
true
,
nullptr
);
if
(
obj_dict
&&
obj_dict
->
cls
==
dict_cls
)
{
result
=
new
BoxedList
();
for
(
auto
&
kv
:
static_cast
<
BoxedDict
*>
(
obj_dict
)
->
d
)
{
...
...
@@ -332,7 +332,7 @@ Box* getattrFunc(Box* obj, Box* _str, Box* default_value) {
}
BoxedString
*
str
=
static_cast
<
BoxedString
*>
(
_str
);
Box
*
rtn
=
getattr_internal
(
obj
,
str
->
s
,
true
,
true
,
NULL
,
NULL
);
Box
*
rtn
=
getattr_internal
(
obj
,
str
->
s
,
true
,
true
,
NULL
);
if
(
!
rtn
)
{
if
(
default_value
)
...
...
@@ -351,7 +351,7 @@ Box* hasattr(Box* obj, Box* _str) {
}
BoxedString
*
str
=
static_cast
<
BoxedString
*>
(
_str
);
Box
*
attr
=
getattr_internal
(
obj
,
str
->
s
,
true
,
true
,
NULL
,
NULL
);
Box
*
attr
=
getattr_internal
(
obj
,
str
->
s
,
true
,
true
,
NULL
);
Box
*
rtn
=
attr
?
True
:
False
;
return
rtn
;
...
...
src/runtime/objmodel.cpp
View file @
50eb5bed
This source diff could not be displayed because it is too large. You can
view the blob
instead.
src/runtime/objmodel.h
View file @
50eb5bed
...
...
@@ -84,10 +84,10 @@ extern "C" BoxedClosure* createClosure(BoxedClosure* parent_closure);
class
BinopRewriteArgs
;
extern
"C"
Box
*
binopInternal
(
Box
*
lhs
,
Box
*
rhs
,
int
op_type
,
bool
inplace
,
BinopRewriteArgs
*
rewrite_args
);
class
CallRewriteArgs
;
Box
*
typeCallInternal
(
BoxedFunction
*
f
,
CallRewriteArgs
*
rewrite_args
,
ArgPassSpec
argspec
,
Box
*
arg1
,
Box
*
arg2
,
Box
*
arg3
,
Box
**
args
,
const
std
::
vector
<
const
std
::
string
*>*
keyword_names
);
class
CallRewriteArgs
;
enum
LookupScope
{
CLASS_ONLY
=
1
,
INST_ONLY
=
2
,
...
...
@@ -97,14 +97,13 @@ extern "C" Box* callattrInternal(Box* obj, const std::string* attr, LookupScope,
ArgPassSpec
argspec
,
Box
*
arg1
,
Box
*
arg2
,
Box
*
arg3
,
Box
**
args
,
const
std
::
vector
<
const
std
::
string
*>*
keyword_names
);
extern
"C"
void
delattr_internal
(
Box
*
obj
,
const
std
::
string
&
attr
,
bool
allow_custom
,
DelattrRewriteArgs
2
*
rewrite_args
);
DelattrRewriteArgs
*
rewrite_args
);
struct
CompareRewriteArgs
;
Box
*
compareInternal
(
Box
*
lhs
,
Box
*
rhs
,
int
op_type
,
CompareRewriteArgs
*
rewrite_args
);
Box
*
getattr_internal
(
Box
*
obj
,
const
std
::
string
&
attr
,
bool
check_cls
,
bool
allow_custom
,
GetattrRewriteArgs
*
rewrite_args
,
GetattrRewriteArgs2
*
rewrite_args2
);
GetattrRewriteArgs
*
rewrite_args
);
Box
*
typeLookup
(
BoxedClass
*
cls
,
const
std
::
string
&
attr
,
GetattrRewriteArgs
*
rewrite_args
,
GetattrRewriteArgs2
*
rewrite_args2
);
Box
*
typeLookup
(
BoxedClass
*
cls
,
const
std
::
string
&
attr
,
GetattrRewriteArgs
*
rewrite_args
);
extern
"C"
void
raiseAttributeErrorStr
(
const
char
*
typeName
,
const
char
*
attr
)
__attribute__
((
__noreturn__
));
extern
"C"
void
raiseAttributeError
(
Box
*
obj
,
const
char
*
attr
)
__attribute__
((
__noreturn__
));
...
...
src/runtime/types.cpp
View file @
50eb5bed
...
...
@@ -84,7 +84,7 @@ extern "C" BoxedFunction::BoxedFunction(CLFunction* f)
// this->giveAttr("__name__", boxString(&f->source->ast->name));
this
->
giveAttr
(
"__name__"
,
boxString
(
f
->
source
->
getName
()));
Box
*
modname
=
f
->
source
->
parent_module
->
getattr
(
"__name__"
,
NULL
,
NULL
);
Box
*
modname
=
f
->
source
->
parent_module
->
getattr
(
"__name__"
,
NULL
);
this
->
giveAttr
(
"__module__"
,
modname
);
}
...
...
@@ -110,7 +110,7 @@ extern "C" BoxedFunction::BoxedFunction(CLFunction* f, std::initializer_list<Box
// this->giveAttr("__name__", boxString(&f->source->ast->name));
this
->
giveAttr
(
"__name__"
,
boxString
(
f
->
source
->
getName
()));
Box
*
modname
=
f
->
source
->
parent_module
->
getattr
(
"__name__"
,
NULL
,
NULL
);
Box
*
modname
=
f
->
source
->
parent_module
->
getattr
(
"__name__"
,
NULL
);
this
->
giveAttr
(
"__module__"
,
modname
);
}
...
...
@@ -516,7 +516,7 @@ Box* objectNew(BoxedClass* cls, BoxedTuple* args) {
if
(
args
->
elts
.
size
()
!=
0
)
{
// TODO slow
if
(
typeLookup
(
cls
,
"__init__"
,
NULL
,
NULL
)
==
typeLookup
(
object_cls
,
"__init__"
,
NULL
,
NULL
))
if
(
typeLookup
(
cls
,
"__init__"
,
NULL
)
==
typeLookup
(
object_cls
,
"__init__"
,
NULL
))
raiseExcHelper
(
TypeError
,
"object.__new__() takes no parameters"
);
}
...
...
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