[GPU] Fix scalar c[#+aL], shader docs/refactoring
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2022 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -16,11 +16,45 @@
|
||||
#include "xenia/base/platform.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
|
||||
// Closest AMD doc:
|
||||
// The XNA Game Studio 3.1 contains Graphics.ShaderCompiler.AssembleFromSource,
|
||||
// which, for TargetPlatform.Xbox360, can validate and assemble Xbox 360 shader
|
||||
// microcode from Xbox 360 and Direct3D 9 shader assembly, returning the binary,
|
||||
// as well as validation warnings and errors and the disassembly via the warning
|
||||
// output. It is the primary source of information about the binary encoding of
|
||||
// the instructions, as well as valid usage of instruction parameters and
|
||||
// sequences.
|
||||
// https://www.microsoft.com/en-us/download/details.aspx?id=39
|
||||
// (XNAGS31_setup.exe)
|
||||
// Xenia provides a tool, tools/shader-playground, that invokes the assembler,
|
||||
// displays the binary and the disassembly from the official assembler, and also
|
||||
// shows the disassembly generated by Xenia, and passes it back to the assembler
|
||||
// to validate Xenia's microcode parsing and disassembly by checking if
|
||||
// reassembling the disassembly results in the same binary.
|
||||
//
|
||||
// The behavior and the parameters of some of the instructions were previously
|
||||
// documented on MSDN in the XNA Game Studio programming guide:
|
||||
// http://web.archive.org/web/20081211005537/http://msdn.microsoft.com/en-us/library/bb313877.aspx
|
||||
//
|
||||
// A great amount of documentation, such as the R400 sequencer specification and
|
||||
// the official emulator code, was made available during the LG Electronics,
|
||||
// Inc. v. ATI Technologies ULC "Multi-thread Graphics Processing System" patent
|
||||
// dispute IPR2015-00325, with the motion to seal having been denied due to "a
|
||||
// strong public policy interest in making all information filed in an inter
|
||||
// partes review publicly available". Most of the documents attached, however,
|
||||
// cover early versions - the development process - of the R400 architecture, so
|
||||
// there are some differences from the final Xenos GPU (DOT2ADDv is defined
|
||||
// differently, for example, and MUL/ADD/SUB_CONST are missing).
|
||||
// https://portal.unifiedpatents.com/ptab/case/IPR2015-00325
|
||||
//
|
||||
// Also, the R600, while having a different 5-scalar, as opposed to vec4|scalar,
|
||||
// parallelism model and instruction encodings and targeting Direct3D 10 rather
|
||||
// that 9, inherits a lot of instructions and architectural concepts from the
|
||||
// R400.
|
||||
// https://www.x.org/docs/AMD/old/r600isa.pdf
|
||||
// https://developer.amd.com/wordpress/media/2012/10/r600isa.pdf
|
||||
// https://developer.amd.com/wordpress/media/2012/10/R600_Instruction_Set_Architecture.pdf
|
||||
// Microcode format differs, but most fields/enums are the same.
|
||||
|
||||
// This code comes from the freedreno project:
|
||||
// Parts of this code also come from the freedreno project:
|
||||
// https://github.com/freedreno/freedreno/blob/master/includes/instr-a2xx.h
|
||||
/*
|
||||
* Copyright (c) 2012 Rob Clark <robdclark@gmail.com>
|
||||
@@ -156,7 +190,8 @@ struct ControlFlowExecInstruction {
|
||||
uint32_t address() const { return address_; }
|
||||
// Number of instructions being executed.
|
||||
uint32_t count() const { return count_; }
|
||||
// Sequence bits, 2 per instruction, indicating whether ALU or fetch.
|
||||
// Sequence bits, 2 per instruction.
|
||||
// [0] - ALU (0) or fetch (1), [1] - serialize.
|
||||
uint32_t sequence() const { return serialize_; }
|
||||
// Whether to reset the current predicate.
|
||||
bool clean() const { return clean_ == 1; }
|
||||
@@ -189,7 +224,8 @@ struct ControlFlowCondExecInstruction {
|
||||
uint32_t address() const { return address_; }
|
||||
// Number of instructions being executed.
|
||||
uint32_t count() const { return count_; }
|
||||
// Sequence bits, 2 per instruction, indicating whether ALU or fetch.
|
||||
// Sequence bits, 2 per instruction.
|
||||
// [0] - ALU (0) or fetch (1), [1] - serialize.
|
||||
uint32_t sequence() const { return serialize_; }
|
||||
// Constant index used as the conditional.
|
||||
uint32_t bool_address() const { return bool_address_; }
|
||||
@@ -224,7 +260,8 @@ struct ControlFlowCondExecPredInstruction {
|
||||
uint32_t address() const { return address_; }
|
||||
// Number of instructions being executed.
|
||||
uint32_t count() const { return count_; }
|
||||
// Sequence bits, 2 per instruction, indicating whether ALU or fetch.
|
||||
// Sequence bits, 2 per instruction.
|
||||
// [0] - ALU (0) or fetch (1), [1] - serialize.
|
||||
uint32_t sequence() const { return serialize_; }
|
||||
// Whether to reset the current predicate.
|
||||
bool clean() const { return clean_ == 1; }
|
||||
@@ -591,6 +628,24 @@ enum class FetchOpcode : uint32_t {
|
||||
kSetTextureGradientsVert = 26,
|
||||
};
|
||||
|
||||
enum class FetchDestinationSwizzle {
|
||||
// The component indices are absolute (not relative to the component itself,
|
||||
// unlike in ALU operation sources).
|
||||
kX = 0,
|
||||
kY = 1,
|
||||
kZ = 2,
|
||||
kW = 3,
|
||||
k0 = 4,
|
||||
k1 = 5,
|
||||
// Keep the current value of the destination register (don't write).
|
||||
kKeep = 7,
|
||||
};
|
||||
|
||||
constexpr FetchDestinationSwizzle GetFetchDestinationComponentSwizzle(
|
||||
uint32_t swizzle, uint32_t component) {
|
||||
return FetchDestinationSwizzle((swizzle >> (3 * component)) & 0b111);
|
||||
}
|
||||
|
||||
struct alignas(uint32_t) VertexFetchInstruction {
|
||||
FetchOpcode opcode() const { return data_.opcode_value; }
|
||||
|
||||
@@ -614,29 +669,6 @@ struct alignas(uint32_t) VertexFetchInstruction {
|
||||
uint32_t src_swizzle() const { return data_.src_swiz; }
|
||||
bool is_src_relative() const { return data_.src_reg_am; }
|
||||
|
||||
// Returns true if the fetch actually fetches data.
|
||||
// This may be false if it's used only to populate constants.
|
||||
bool fetches_any_data() const {
|
||||
uint32_t dst_swiz = data_.dst_swiz;
|
||||
bool fetches_any_data = false;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if ((dst_swiz & 0x7) == 4) {
|
||||
// 0.0
|
||||
} else if ((dst_swiz & 0x7) == 5) {
|
||||
// 1.0
|
||||
} else if ((dst_swiz & 0x7) == 6) {
|
||||
// ?
|
||||
} else if ((dst_swiz & 0x7) == 7) {
|
||||
// Previous register value.
|
||||
} else {
|
||||
fetches_any_data = true;
|
||||
break;
|
||||
}
|
||||
dst_swiz >>= 3;
|
||||
}
|
||||
return fetches_any_data;
|
||||
}
|
||||
|
||||
uint32_t prefetch_count() const { return data_.prefetch_count; }
|
||||
bool is_mini_fetch() const { return data_.is_mini_fetch == 1; }
|
||||
|
||||
@@ -676,6 +708,7 @@ struct alignas(uint32_t) VertexFetchInstruction {
|
||||
uint32_t const_index_sel : 2;
|
||||
// Prefetch count minus 1.
|
||||
uint32_t prefetch_count : 3;
|
||||
// Absolute, one component.
|
||||
uint32_t src_swiz : 2;
|
||||
};
|
||||
struct {
|
||||
@@ -769,10 +802,11 @@ struct alignas(uint32_t) TextureFetchInstruction {
|
||||
uint32_t fetch_valid_only : 1;
|
||||
uint32_t const_index : 5;
|
||||
uint32_t tx_coord_denorm : 1;
|
||||
uint32_t src_swiz : 6; // xyz
|
||||
// Absolute, three components.
|
||||
uint32_t src_swiz : 6;
|
||||
};
|
||||
struct {
|
||||
uint32_t dst_swiz : 12; // xyzw
|
||||
uint32_t dst_swiz : 12;
|
||||
xenos::TextureFilter mag_filter : 2;
|
||||
xenos::TextureFilter min_filter : 2;
|
||||
xenos::TextureFilter mip_filter : 2;
|
||||
@@ -801,21 +835,96 @@ struct alignas(uint32_t) TextureFetchInstruction {
|
||||
};
|
||||
static_assert_size(TextureFetchInstruction, sizeof(uint32_t) * 3);
|
||||
|
||||
union alignas(uint32_t) FetchInstruction {
|
||||
public:
|
||||
FetchOpcode opcode() const { return data_.opcode_value; }
|
||||
|
||||
// Whether the jump is predicated (or conditional).
|
||||
bool is_predicated() const { return data_.is_predicated; }
|
||||
// Required condition value of the comparision (true or false).
|
||||
bool predicate_condition() const { return data_.pred_condition == 1; }
|
||||
|
||||
uint32_t dest() const { return data_.dst_reg; }
|
||||
uint32_t dest_swizzle() const { return data_.dst_swiz; }
|
||||
bool is_dest_relative() const { return data_.dst_reg_am; }
|
||||
uint32_t src() const { return data_.src_reg; }
|
||||
bool is_src_relative() const { return data_.src_reg_am; }
|
||||
|
||||
// For FetchOpcode::kVertexFetch.
|
||||
const VertexFetchInstruction& vertex_fetch() const { return vertex_fetch_; }
|
||||
// For operations other than FetchOpcode::kVertexFetch.
|
||||
const TextureFetchInstruction& texture_fetch() const {
|
||||
return texture_fetch_;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Data {
|
||||
struct {
|
||||
FetchOpcode opcode_value : 5;
|
||||
uint32_t src_reg : 6;
|
||||
uint32_t src_reg_am : 1;
|
||||
uint32_t dst_reg : 6;
|
||||
uint32_t dst_reg_am : 1;
|
||||
// Specific to vertex or texture fetch.
|
||||
uint32_t : 1;
|
||||
// [0-31], points to one tf# or three vf# constants.
|
||||
uint32_t const_index : 5;
|
||||
// Specific to vertex or texture fetch.
|
||||
uint32_t : 7;
|
||||
};
|
||||
struct {
|
||||
uint32_t dst_swiz : 12;
|
||||
// Specific to vertex or texture fetch.
|
||||
uint32_t : 19;
|
||||
uint32_t is_predicated : 1;
|
||||
};
|
||||
struct {
|
||||
// Specific to vertex or texture fetch.
|
||||
uint32_t : 31;
|
||||
uint32_t pred_condition : 1;
|
||||
};
|
||||
};
|
||||
Data data_;
|
||||
VertexFetchInstruction vertex_fetch_;
|
||||
TextureFetchInstruction texture_fetch_;
|
||||
};
|
||||
static_assert_size(FetchInstruction, sizeof(uint32_t) * 3);
|
||||
|
||||
// What follows is largely a mash up of the microcode assembly naming and the
|
||||
// R600 docs that have a near 1:1 with the instructions available in the xenos
|
||||
// R600 docs that have a near 1:1 with the instructions available in the Xenos
|
||||
// GPU, and Adreno 2xx instruction names found in Freedreno. Some of the
|
||||
// behavior has been experimentally verified. Some has been guessed.
|
||||
// Docs: https://www.x.org/docs/AMD/old/r600isa.pdf
|
||||
// behavior has been experimentally verified. Some has been guessed. Some
|
||||
// instructions are implemented in the Exhibit 2092 - sq_alu of IPR2015-00325,
|
||||
// however, the code provided there is early and incomplete.
|
||||
//
|
||||
// Conventions:
|
||||
// - All temporary registers are vec4s.
|
||||
// - Scalar ops swizzle out a single component of their source registers denoted
|
||||
// by 'a' or 'b'. src0.a means 'the first component specified for src0' and
|
||||
// src0.ab means 'two components specified for src0, in order'.
|
||||
// - Scalar ops write the result to the entire destination register.
|
||||
// - pv and ps are the previous results of a vector or scalar ALU operation.
|
||||
// Both are valid only within the current ALU clause. They are not modified
|
||||
// when the instruction that would write them fails its predication check.
|
||||
// - Most scalar ALU operations work with one or two components of the source
|
||||
// register passed as the third operand of the whole co-issued ALU operation,
|
||||
// denoted by `a` (the left-hand operand) and `b` (the right-hand operand).
|
||||
// `a` is the [(3 + src3_swizzle[6:7]) & 3] component (W - alpha).
|
||||
// `b` is the [(0 + src3_swizzle[0:1]) & 3] component (X - red).
|
||||
// - mulsc, addsc, subsc scalar ALU operations accept two operands - a float
|
||||
// constant with the `a` (W) swizzle (addressed by the third operand index and
|
||||
// addressing mode) being the left-hand operand, and a temporary register with
|
||||
// the `b` (X) swizzle with the index constructed from:
|
||||
// - [0:0] = scalar_opcode[0:0]
|
||||
// - [1:1] = src3_sel[0:0]
|
||||
// - [2:5] = src3_swizzle[2:5]
|
||||
// abs_constants and third source's negation are applied to both the constant
|
||||
// and the temporary register.
|
||||
// - Some scalar ALU instructions don't have operands.
|
||||
// - Scalar ALU operations replicate the result into all masked components.
|
||||
// - Overall, the WXYZ order is pretty commonly used in the Exhibit 2092 -
|
||||
// sq_alu of IPR2015-00325, this is where the AB = WX order of scalar operands
|
||||
// likely comes from. Vector predicate instructions also involve the W and X
|
||||
// components, and in IPR2015-00325 sq_alu, individual components in the
|
||||
// emulated vector instructions are handled in the WXYZ order. However, max4's
|
||||
// "greater than the rest" check order is RGBA (XYZW) there. dp4, though, sums
|
||||
// the products in WXYZ order in IPR2015-00325 sq_alu (but in XYZW order on
|
||||
// MSDN).
|
||||
// - ps is the previous result of a scalar ALU operation. It is not modified
|
||||
// when the instruction that would write it fails its predication check.
|
||||
// - Direct3D 9 rules (like in GCN v_*_legacy_f32 instructions) for
|
||||
// multiplication (+-0 or denormal * anything = +0) wherever it's present
|
||||
// (mul, mad, dp, etc.) and for NaN in min/max. It's very important to respect
|
||||
@@ -1137,6 +1246,9 @@ enum class AluScalarOpcode : uint32_t {
|
||||
// dest.xyzw = sqrt(src0.a);
|
||||
kSqrt = 40,
|
||||
|
||||
// 0 and 1 are the same instruction - one bit of the register index is stored
|
||||
// in the opcode field.
|
||||
|
||||
// mulsc/MUL_CONST_0 dest, src0.a, src1.a
|
||||
kMulsc0 = 42,
|
||||
// mulsc/MUL_CONST_1 dest, src0.a, src1.a
|
||||
@@ -1303,19 +1415,24 @@ enum class AluVectorOpcode : uint32_t {
|
||||
// dp4/DOT4v dest, src0, src1
|
||||
// dest.xyzw = src0.x * src1.x + src0.y * src1.y + src0.z * src1.z +
|
||||
// src0.w * src1.w;
|
||||
// Note: only pv.x contains the value.
|
||||
kDp4 = 15,
|
||||
|
||||
// Three-Element Dot Product
|
||||
// dp3/DOT3v dest, src0, src1
|
||||
// dest.xyzw = src0.x * src1.x + src0.y * src1.y + src0.z * src1.z;
|
||||
// Note: only pv.x contains the value.
|
||||
kDp3 = 16,
|
||||
|
||||
// Two-Element Dot Product and Add
|
||||
// dp2add/DOT2ADDv dest, src0, src1, src2
|
||||
// dest.xyzw = src0.x * src1.x + src0.y * src1.y + src2.x;
|
||||
// Note: only pv.x contains the value.
|
||||
// IPR2015-00325 sq_alu may be an outdated and unreliable reference (Sequencer
|
||||
// Parts Development folder history lists a few changes regarding the swizzle
|
||||
// in dot2add, sq_alu though implements the instruction as
|
||||
// src0.x * src1.x + src0.z * src1.z + src2.y, but MSDN specifies the correct
|
||||
// order as provided in the beginning of this comment, further proven by
|
||||
// assembling PC shader assembly using XNA, with Shader Model 2 dp2add being
|
||||
// translated directly into Xenos dp2add without additional swizzling).
|
||||
// http://web.archive.org/web/20100705150552/http://msdn.microsoft.com/en-us/library/bb313922.aspx
|
||||
kDp2Add = 17,
|
||||
|
||||
// Cube Map
|
||||
@@ -1363,8 +1480,16 @@ enum class AluVectorOpcode : uint32_t {
|
||||
|
||||
// Four-Element Maximum
|
||||
// max4/MAX4v dest, src0
|
||||
// dest.xyzw = max(src0.x, src0.y, src0.z, src0.w);
|
||||
// Note: only pv.x contains the value.
|
||||
// According to IPR2015-00325 sq_alu:
|
||||
// if (src0.x > src0.y && src0.x > src0.z && src0.x > src0.w) {
|
||||
// dest.xyzw = src0.x;
|
||||
// } else if (src0.y > src0.z && src0.y > src0.w) {
|
||||
// dest.xyzw = src0.y;
|
||||
// } else if (src0.z > src0.w) {
|
||||
// dest.xyzw = src0.z;
|
||||
// } else {
|
||||
// dest.xyzw = src0.w;
|
||||
// }
|
||||
kMax4 = 19,
|
||||
|
||||
// Floating-Point Predicate Counter Increment If Equal
|
||||
@@ -1672,7 +1797,9 @@ struct alignas(uint32_t) AluInstruction {
|
||||
bool abs_constants() const { return data_.abs_constants == 1; }
|
||||
bool is_const_0_addressed() const { return data_.const_0_rel_abs == 1; }
|
||||
bool is_const_1_addressed() const { return data_.const_1_rel_abs == 1; }
|
||||
bool is_address_relative() const { return data_.address_absolute == 1; }
|
||||
bool is_const_address_register_relative() const {
|
||||
return data_.const_address_register_relative == 1;
|
||||
}
|
||||
|
||||
AluVectorOpcode vector_opcode() const { return data_.vector_opc; }
|
||||
uint32_t vector_write_mask() const { return data_.vector_write_mask; }
|
||||
@@ -1686,6 +1813,18 @@ struct alignas(uint32_t) AluInstruction {
|
||||
bool is_scalar_dest_relative() const { return data_.scalar_dest_rel == 1; }
|
||||
bool scalar_clamp() const { return data_.scalar_clamp == 1; }
|
||||
|
||||
static constexpr uint32_t src_temp_reg(uint32_t src_reg) {
|
||||
return src_reg & 0x3F;
|
||||
}
|
||||
static constexpr bool is_src_temp_relative(uint32_t src_reg) {
|
||||
return (src_reg & 0x40) != 0;
|
||||
}
|
||||
static constexpr bool is_src_temp_value_absolute(uint32_t src_reg) {
|
||||
return (src_reg & 0x80) != 0;
|
||||
}
|
||||
// Full register index for constants, packed structure for temporary
|
||||
// registers (unpack using src_temp_reg, is_src_temp_relative,
|
||||
// is_src_temp_value_absolute).
|
||||
uint32_t src_reg(size_t i) const {
|
||||
switch (i) {
|
||||
case 1:
|
||||
@@ -1702,16 +1841,59 @@ struct alignas(uint32_t) AluInstruction {
|
||||
bool src_is_temp(size_t i) const {
|
||||
switch (i) {
|
||||
case 1:
|
||||
return data_.src1_sel == 1;
|
||||
return bool(data_.src1_sel);
|
||||
case 2:
|
||||
return data_.src2_sel == 1;
|
||||
return bool(data_.src2_sel);
|
||||
case 3:
|
||||
return data_.src3_sel == 1;
|
||||
return bool(data_.src3_sel);
|
||||
default:
|
||||
assert_unhandled_case(i);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// Whether the specified operand is actually a constant is disregarded in this
|
||||
// function so its scope is limited to just parsing the structure's layout -
|
||||
// to decide whether to use relative addressing for the operand as a whole,
|
||||
// check externally whether the operand is actually a constant first.
|
||||
//
|
||||
// For the constant operand in mulsc, addsc, subsc, this should be called for
|
||||
// the operand index 3. Note that the XNA disassembler takes the addressing
|
||||
// mode for the constant scalar operand unconditionally from const_1_rel_abs,
|
||||
// and ignores the +aL for it unless the scalar operation is co-issued with a
|
||||
// vector operation reading from a constant. However, the XNA assembler treats
|
||||
// the constant scalar operand as a constant in the third operand, and places
|
||||
// the addressing mode for it in const_0_rel_abs if no other constants are
|
||||
// used in the whole ALU instruction. The validator also doesn't report
|
||||
// anything if +aL is used when the constant scalar operand is the only
|
||||
// constant in the instruction (and explicitly calls it the third constant in
|
||||
// the error message in case both vector operands are constants, and different
|
||||
// addressing modes are used for the second vector operand and the constant
|
||||
// scalar operand). Passing the disassembly produced by XNA back to the
|
||||
// assembler results in different microcode in this case. This indicates that
|
||||
// most likely there's a bug in the XNA disassembler, and that the addressing
|
||||
// mode for the constant scalar operand should actually be taken the same way
|
||||
// as for the third vector operand - from const_0_rel_abs if there are no
|
||||
// constant vector operands, or from const_1_rel_abs if there is at least one.
|
||||
bool src_const_is_addressed(size_t i) const {
|
||||
// "error X7100: When three constants are used in one instruction, the
|
||||
// second and third constant must either both be non-relative, or both be
|
||||
// relative."
|
||||
// Whether to use const_0_rel_abs or const_1_rel_abs is essentially
|
||||
// min(sum of whether the previous operands are constants, 1).
|
||||
switch (i) {
|
||||
case 1:
|
||||
return bool(data_.const_0_rel_abs);
|
||||
case 2:
|
||||
return bool(src_is_temp(1) ? data_.const_0_rel_abs
|
||||
: data_.const_1_rel_abs);
|
||||
case 3:
|
||||
return bool((src_is_temp(1) && src_is_temp(2)) ? data_.const_0_rel_abs
|
||||
: data_.const_1_rel_abs);
|
||||
default:
|
||||
assert_unhandled_case(i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
uint32_t src_swizzle(size_t i) const {
|
||||
switch (i) {
|
||||
case 1:
|
||||
@@ -1739,8 +1921,20 @@ struct alignas(uint32_t) AluInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t scalar_const_op_src_temp_reg() const {
|
||||
return (uint32_t(data_.scalar_opc) & 1) | (data_.src3_sel << 1) |
|
||||
(data_.src3_swiz & 0x3C);
|
||||
}
|
||||
|
||||
// Helpers.
|
||||
|
||||
// Returns the absolute component index calculated from the relative swizzle
|
||||
// in an ALU instruction.
|
||||
static constexpr uint32_t GetSwizzledComponentIndex(
|
||||
uint32_t swizzle, uint32_t component_index) {
|
||||
return ((swizzle >> (2 * component_index)) + component_index) & 3;
|
||||
}
|
||||
|
||||
// Note that even if the export component is unused (like W of the vertex
|
||||
// shader misc register, YZW of pixel shader depth), it must still not be
|
||||
// excluded - that may make disassembly not reassemblable if there are
|
||||
@@ -1803,6 +1997,7 @@ struct alignas(uint32_t) AluInstruction {
|
||||
AluScalarOpcode scalar_opc : 6;
|
||||
};
|
||||
struct {
|
||||
// Swizzles are component-relative.
|
||||
uint32_t src3_swiz : 8;
|
||||
uint32_t src2_swiz : 8;
|
||||
uint32_t src1_swiz : 8;
|
||||
@@ -1811,7 +2006,9 @@ struct alignas(uint32_t) AluInstruction {
|
||||
uint32_t src1_reg_negate : 1;
|
||||
uint32_t pred_condition : 1;
|
||||
uint32_t is_predicated : 1;
|
||||
uint32_t address_absolute : 1;
|
||||
// Temporary registers can have only absolute and aL-relative indices, not
|
||||
// a0-relative.
|
||||
uint32_t const_address_register_relative : 1;
|
||||
uint32_t const_1_rel_abs : 1;
|
||||
uint32_t const_0_rel_abs : 1;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user