Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
S
Suyu
Manage
Activity
Members
Labels
Plan
Issues
0
Issue boards
Milestones
Wiki
Code
Merge requests
0
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Terms and privacy
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
many-archive
Suyu
Commits
4632791a
There was an error fetching the commit references. Please try again later.
Commit
4632791a
authored
9 years ago
by
bunnei
Browse files
Options
Downloads
Patches
Plain Diff
shader_jit_x64: Rewrite flow control to support arbitrary CALL and JMP instructions.
parent
135aec7b
No related branches found
No related tags found
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
src/video_core/shader/shader_jit_x64.cpp
+92
-30
92 additions, 30 deletions
src/video_core/shader/shader_jit_x64.cpp
src/video_core/shader/shader_jit_x64.h
+27
-5
27 additions, 5 deletions
src/video_core/shader/shader_jit_x64.h
with
119 additions
and
35 deletions
src/video_core/shader/shader_jit_x64.cpp
+
92
−
30
View file @
4632791a
...
@@ -137,6 +137,15 @@ static const u8 NO_SRC_REG_SWIZZLE = 0x1b;
...
@@ -137,6 +137,15 @@ static const u8 NO_SRC_REG_SWIZZLE = 0x1b;
/// Raw constant for the destination register enable mask that indicates all components are enabled
/// Raw constant for the destination register enable mask that indicates all components are enabled
static
const
u8
NO_DEST_REG_MASK
=
0xf
;
static
const
u8
NO_DEST_REG_MASK
=
0xf
;
/**
* Get the vertex shader instruction for a given offset in the current shader program
* @param offset Offset in the current shader program of the instruction
* @return Instruction at the specified offset
*/
static
Instruction
GetVertexShaderInstruction
(
size_t
offset
)
{
return
{
g_state
.
vs
.
program_code
[
offset
]
};
}
/**
/**
* Loads and swizzles a source register into the specified XMM register.
* Loads and swizzles a source register into the specified XMM register.
* @param instr VS instruction, used for determining how to load the source register
* @param instr VS instruction, used for determining how to load the source register
...
@@ -564,10 +573,23 @@ void JitCompiler::Compile_END(Instruction instr) {
...
@@ -564,10 +573,23 @@ void JitCompiler::Compile_END(Instruction instr) {
}
}
void
JitCompiler
::
Compile_CALL
(
Instruction
instr
)
{
void
JitCompiler
::
Compile_CALL
(
Instruction
instr
)
{
unsigned
offset
=
instr
.
flow_control
.
dest_offset
;
// Need to advance the return address past the proceeding instructions, this is the number of bytes to skip
while
(
offset
<
(
instr
.
flow_control
.
dest_offset
+
instr
.
flow_control
.
num_instructions
))
{
constexpr
unsigned
SKIP
=
21
;
Compile_NextInstr
(
&
offset
);
const
uintptr_t
start
=
reinterpret_cast
<
uintptr_t
>
(
GetCodePtr
());
}
// Push return address - not using CALL because we also want to push the offset of the return before jumping
MOV
(
64
,
R
(
RAX
),
ImmPtr
(
GetCodePtr
()
+
SKIP
));
PUSH
(
RAX
);
// Push offset of the return
PUSH
(
32
,
Imm32
(
instr
.
flow_control
.
dest_offset
+
instr
.
flow_control
.
num_instructions
));
// Jump
FixupBranch
b
=
J
(
true
);
fixup_branches
.
push_back
({
b
,
instr
.
flow_control
.
dest_offset
});
// Make sure that if the above code changes, SKIP gets updated
ASSERT
(
reinterpret_cast
<
uintptr_t
>
(
GetCodePtr
())
-
start
==
SKIP
);
}
}
void
JitCompiler
::
Compile_CALLC
(
Instruction
instr
)
{
void
JitCompiler
::
Compile_CALLC
(
Instruction
instr
)
{
...
@@ -645,8 +667,8 @@ void JitCompiler::Compile_MAD(Instruction instr) {
...
@@ -645,8 +667,8 @@ void JitCompiler::Compile_MAD(Instruction instr) {
}
}
void
JitCompiler
::
Compile_IF
(
Instruction
instr
)
{
void
JitCompiler
::
Compile_IF
(
Instruction
instr
)
{
ASSERT_MSG
(
instr
.
flow_control
.
dest_offset
>
*
offset_pt
r
,
"Backwards if-statements (%d -> %d) not supported"
,
ASSERT_MSG
(
instr
.
flow_control
.
dest_offset
>
last_program_counte
r
,
"Backwards if-statements (%d -> %d) not supported"
,
*
offset_pt
r
,
instr
.
flow_control
.
dest_offset
.
Value
());
last_program_counte
r
,
instr
.
flow_control
.
dest_offset
.
Value
());
// Evaluate the "IF" condition
// Evaluate the "IF" condition
if
(
instr
.
opcode
.
Value
()
==
OpCode
::
Id
::
IFU
)
{
if
(
instr
.
opcode
.
Value
()
==
OpCode
::
Id
::
IFU
)
{
...
@@ -677,8 +699,8 @@ void JitCompiler::Compile_IF(Instruction instr) {
...
@@ -677,8 +699,8 @@ void JitCompiler::Compile_IF(Instruction instr) {
}
}
void
JitCompiler
::
Compile_LOOP
(
Instruction
instr
)
{
void
JitCompiler
::
Compile_LOOP
(
Instruction
instr
)
{
ASSERT_MSG
(
instr
.
flow_control
.
dest_offset
>
*
offset_pt
r
,
"Backwards loops (%d -> %d) not supported"
,
ASSERT_MSG
(
instr
.
flow_control
.
dest_offset
>
last_program_counte
r
,
"Backwards loops (%d -> %d) not supported"
,
*
offset_pt
r
,
instr
.
flow_control
.
dest_offset
.
Value
());
last_program_counte
r
,
instr
.
flow_control
.
dest_offset
.
Value
());
ASSERT_MSG
(
!
looping
,
"Nested loops not supported"
);
ASSERT_MSG
(
!
looping
,
"Nested loops not supported"
);
looping
=
true
;
looping
=
true
;
...
@@ -706,9 +728,6 @@ void JitCompiler::Compile_LOOP(Instruction instr) {
...
@@ -706,9 +728,6 @@ void JitCompiler::Compile_LOOP(Instruction instr) {
}
}
void
JitCompiler
::
Compile_JMP
(
Instruction
instr
)
{
void
JitCompiler
::
Compile_JMP
(
Instruction
instr
)
{
ASSERT_MSG
(
instr
.
flow_control
.
dest_offset
>
*
offset_ptr
,
"Backwards jumps (%d -> %d) not supported"
,
*
offset_ptr
,
instr
.
flow_control
.
dest_offset
.
Value
());
if
(
instr
.
opcode
.
Value
()
==
OpCode
::
Id
::
JMPC
)
if
(
instr
.
opcode
.
Value
()
==
OpCode
::
Id
::
JMPC
)
Compile_EvaluateCondition
(
instr
);
Compile_EvaluateCondition
(
instr
);
else
if
(
instr
.
opcode
.
Value
()
==
OpCode
::
Id
::
JMPU
)
else
if
(
instr
.
opcode
.
Value
()
==
OpCode
::
Id
::
JMPU
)
...
@@ -718,31 +737,42 @@ void JitCompiler::Compile_JMP(Instruction instr) {
...
@@ -718,31 +737,42 @@ void JitCompiler::Compile_JMP(Instruction instr) {
bool
inverted_condition
=
(
instr
.
opcode
.
Value
()
==
OpCode
::
Id
::
JMPU
)
&&
bool
inverted_condition
=
(
instr
.
opcode
.
Value
()
==
OpCode
::
Id
::
JMPU
)
&&
(
instr
.
flow_control
.
num_instructions
&
1
);
(
instr
.
flow_control
.
num_instructions
&
1
);
FixupBranch
b
=
J_CC
(
inverted_condition
?
CC_Z
:
CC_NZ
,
true
);
Compile_Block
(
instr
.
flow_control
.
dest_offset
);
SetJumpTarget
(
b
);
FixupBranch
b
=
J_CC
(
inverted_condition
?
CC_Z
:
CC_NZ
,
true
);
fixup_branches
.
push_back
({
b
,
instr
.
flow_control
.
dest_offset
});
}
}
void
JitCompiler
::
Compile_Block
(
unsigned
end
)
{
void
JitCompiler
::
Compile_Block
(
unsigned
end
)
{
// Save current offset pointer
while
(
program_counter
<
end
)
{
unsigned
*
prev_offset_ptr
=
offset_ptr
;
Compile_NextInstr
();
unsigned
offset
=
*
prev_offset_ptr
;
}
}
while
(
offset
<
end
)
void
JitCompiler
::
Compile_Return
()
{
Compile_NextInstr
(
&
offset
);
// Peek return offset on the stack and check if we're at that offset
MOV
(
64
,
R
(
RAX
),
MDisp
(
RSP
,
0
));
CMP
(
32
,
R
(
RAX
),
Imm32
(
program_counter
));
// Restore current offset pointer
// If so, jump back to before CALL
offset_ptr
=
prev_offset_ptr
;
FixupBranch
b
=
J_CC
(
CC_NZ
,
true
);
*
offset_ptr
=
offset
;
ADD
(
64
,
R
(
RSP
),
Imm32
(
8
));
// Ignore return offset that's on the stack
POP
(
RAX
);
// Pop off return address
JMPptr
(
R
(
RAX
));
SetJumpTarget
(
b
);
}
}
void
JitCompiler
::
Compile_NextInstr
(
unsigned
*
offset
)
{
void
JitCompiler
::
Compile_NextInstr
()
{
offset_ptr
=
offset
;
last_program_counter
=
program_counter
;
auto
search
=
return_offsets
.
find
(
program_counter
);
if
(
search
!=
return_offsets
.
end
())
{
Compile_Return
();
}
ASSERT_MSG
(
code_ptr
[
program_counter
]
==
nullptr
,
"Tried to compile already compiled shader location!"
);
code_ptr
[
program_counter
]
=
GetCodePtr
();
Instruction
instr
;
Instruction
instr
=
GetVertexShaderInstruction
(
program_counter
++
);
std
::
memcpy
(
&
instr
,
&
g_state
.
vs
.
program_code
[(
*
offset_ptr
)
++
],
sizeof
(
Instruction
));
OpCode
::
Id
opcode
=
instr
.
opcode
.
Value
();
OpCode
::
Id
opcode
=
instr
.
opcode
.
Value
();
auto
instr_func
=
instr_table
[
static_cast
<
unsigned
>
(
opcode
)];
auto
instr_func
=
instr_table
[
static_cast
<
unsigned
>
(
opcode
)];
...
@@ -757,9 +787,24 @@ void JitCompiler::Compile_NextInstr(unsigned* offset) {
...
@@ -757,9 +787,24 @@ void JitCompiler::Compile_NextInstr(unsigned* offset) {
}
}
}
}
void
JitCompiler
::
FindReturnOffsets
()
{
return_offsets
.
clear
();
for
(
size_t
offset
=
0
;
offset
<
g_state
.
vs
.
program_code
.
size
();
++
offset
)
{
Instruction
instr
=
GetVertexShaderInstruction
(
offset
);
switch
(
instr
.
opcode
.
Value
())
{
case
OpCode
::
Id
::
CALL
:
case
OpCode
::
Id
::
CALLC
:
case
OpCode
::
Id
::
CALLU
:
return_offsets
.
insert
(
instr
.
flow_control
.
dest_offset
+
instr
.
flow_control
.
num_instructions
);
break
;
}
}
}
CompiledShader
*
JitCompiler
::
Compile
()
{
CompiledShader
*
JitCompiler
::
Compile
()
{
const
u8
*
start
=
GetCodePtr
();
const
u8
*
start
=
GetCodePtr
();
unsigned
offset
=
g_state
.
regs
.
vs
.
main_offset
;
// The stack pointer is 8 modulo 16 at the entry of a procedure
// The stack pointer is 8 modulo 16 at the entry of a procedure
ABI_PushRegistersAndAdjustStack
(
ABI_ALL_CALLEE_SAVED
,
8
);
ABI_PushRegistersAndAdjustStack
(
ABI_ALL_CALLEE_SAVED
,
8
);
...
@@ -782,10 +827,27 @@ CompiledShader* JitCompiler::Compile() {
...
@@ -782,10 +827,27 @@ CompiledShader* JitCompiler::Compile() {
MOV
(
PTRBITS
,
R
(
RAX
),
ImmPtr
(
&
neg
));
MOV
(
PTRBITS
,
R
(
RAX
),
ImmPtr
(
&
neg
));
MOVAPS
(
NEGBIT
,
MatR
(
RAX
));
MOVAPS
(
NEGBIT
,
MatR
(
RAX
));
// Find all `CALL` instructions and identify return locations
FindReturnOffsets
();
// Reset flow control state
last_program_counter
=
0
;
program_counter
=
0
;
looping
=
false
;
looping
=
false
;
code_ptr
.
fill
(
nullptr
);
fixup_branches
.
clear
();
// Jump to start of the shader program
if
(
g_state
.
regs
.
vs
.
main_offset
!=
0
)
{
fixup_branches
.
push_back
({
J
(
true
),
g_state
.
regs
.
vs
.
main_offset
});
}
// Compile entire program
Compile_Block
(
static_cast
<
unsigned
>
(
g_state
.
vs
.
program_code
.
size
()));
while
(
offset
<
g_state
.
vs
.
program_code
.
size
())
{
// Set the target for any incomplete branches now that the entire shader program has been emitted
Compile_NextInstr
(
&
offset
);
for
(
const
auto
&
branch
:
fixup_branches
)
{
SetJumpTarget
(
branch
.
first
,
code_ptr
[
branch
.
second
]);
}
}
return
(
CompiledShader
*
)
start
;
return
(
CompiledShader
*
)
start
;
...
...
This diff is collapsed.
Click to expand it.
src/video_core/shader/shader_jit_x64.h
+
27
−
5
View file @
4632791a
...
@@ -4,6 +4,9 @@
...
@@ -4,6 +4,9 @@
#pragma once
#pragma once
#include
<set>
#include
<utility>
#include
<nihstro/shader_bytecode.h>
#include
<nihstro/shader_bytecode.h>
#include
"common/x64/emitter.h"
#include
"common/x64/emitter.h"
...
@@ -66,8 +69,9 @@ public:
...
@@ -66,8 +69,9 @@ public:
void
Compile_MAD
(
Instruction
instr
);
void
Compile_MAD
(
Instruction
instr
);
private:
private:
void
Compile_Block
(
unsigned
end
);
void
Compile_Block
(
unsigned
end
);
void
Compile_NextInstr
(
unsigned
*
offset
);
void
Compile_NextInstr
();
void
Compile_SwizzleSrc
(
Instruction
instr
,
unsigned
src_num
,
SourceRegister
src_reg
,
Gen
::
X64Reg
dest
);
void
Compile_SwizzleSrc
(
Instruction
instr
,
unsigned
src_num
,
SourceRegister
src_reg
,
Gen
::
X64Reg
dest
);
void
Compile_DestEnable
(
Instruction
instr
,
Gen
::
X64Reg
dest
);
void
Compile_DestEnable
(
Instruction
instr
,
Gen
::
X64Reg
dest
);
...
@@ -81,13 +85,31 @@ private:
...
@@ -81,13 +85,31 @@ private:
void
Compile_EvaluateCondition
(
Instruction
instr
);
void
Compile_EvaluateCondition
(
Instruction
instr
);
void
Compile_UniformCondition
(
Instruction
instr
);
void
Compile_UniformCondition
(
Instruction
instr
);
/**
* Emits the code to conditionally return from a subroutine envoked by the `CALL` instruction.
*/
void
Compile_Return
();
BitSet32
PersistentCallerSavedRegs
();
BitSet32
PersistentCallerSavedRegs
();
/// Pointer to the variable that stores the current Pica code offset. Used to handle nested code blocks.
/**
unsigned
*
offset_ptr
=
nullptr
;
* Analyzes the entire shader program for `CALL` instructions before emitting any code,
* identifying the locations where a return needs to be inserted.
*/
void
FindReturnOffsets
();
/// Mapping of Pica VS instructions to pointers in the emitted code
std
::
array
<
const
u8
*
,
1024
>
code_ptr
;
/// Offsets in code where a return needs to be inserted
std
::
set
<
unsigned
>
return_offsets
;
unsigned
last_program_counter
;
///< Offset of the most recent instruction decoded
unsigned
program_counter
;
///< Offset of the next instruction to decode
bool
looping
=
false
;
///< True if compiling a loop, used to check for nested loops
///
Set to true if currently in a loop, used to check for the existence of nested loops
///
Branches that need to be fixed up once the entire shader program is compiled
bool
looping
=
false
;
std
::
vector
<
std
::
pair
<
Gen
::
FixupBranch
,
unsigned
>>
fixup_branches
;
};
};
}
// Shader
}
// Shader
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment