Skip to content

Commit 4ffb922

Browse files
authored
Update documentation for v1.0 release.
1 parent 2a483b8 commit 4ffb922

2 files changed

Lines changed: 73 additions & 107 deletions

File tree

Lines changed: 69 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1,187 +1,153 @@
1-
# Tutorial: "Decoding RDNA Instructions with IsaDecoder" #
2-
3-
## Motivation ##
1+
# Decoding RDNA™/CDNA™ Instructions with IsaDecoder API
42

3+
## Motivation
54
AMD recently released its machine-readable GPU ISA specification - a set of XML files describing its RDNA and CDNA Instruction Set Architectures. While you can parse the XML files yourself, the easiest way to consume the specification is using the `IsaDecoder` API: Given an XML specification file, the API can read and parse it for you and even decode single instructions or whole shaders.
65

76
This tutorial demonstrates how to use the `IsaDecoder` API to decode AMD GPU assembly provided in either binary or textual (disassembly) format.
87

9-
## `IsaDecoder` ##
10-
8+
## Getting started with `IsaDecoder` API
119
The API allows you to decode either a single instruction or a whole shader/kernel.
1210

1311
The source code for the API can be found on the [isa_spec_manager GitHub repository](https://github.com/GPUOpen-Tools/isa_spec_manager).
1412

15-
## Getting started - `IsaDecoder` ##
13+
### Step 1: Instantiate `amdisa::IsaDecoder`.
1614

17-
Step 1: Instantiate `IsaDecoder`.
15+
`IsaDecoder` is defined under the namespace amdisa. For this tutorial, we define an `IsaDecoder` object named "decoder".
1816

19-
`IsaDecoder` is defined under the namespace amdisa. For this tutorial, we define an `IsaDecoder` object named "api_decoder".
2017
```c
21-
amdisa::IsaDecoder api_decoder;
18+
amdisa::IsaDecoder decoder;
2219
```
2320

24-
Step 2: Initialize the `IsaDecoder` object with an input XML specification file. Make sure that the XML file you are using matches the GPU architecture that you are about to decode instructions for. For instance, use the MI-300 XML file to decode MI-300 kernels.
21+
### Step 2: Initialize the decoder.
22+
Initialize the `decoder` with an input XML specification file. Make sure that the XML file you are using matches the GPU architecture that you are about to decode instructions for. For instance, use the MI-300/CDNA 3 XML file to decode MI-300/CDNA 3 kernels.
2523

2624
The `Initialize()` API function reads and parses the given XML specification file. Upon success, the `IsaDecoder` object is ready to decode instructions.
25+
2726
```c
2827
bool is_success = api_decoder.Initialize(kPathToSpec, error_msg);
2928
```
29+
## A. Decoding a single instruction in textual format
3030

31-
## 1. Decoding a single instruction ##
31+
Now, when we have the decoder initialized, let's learn how we can use API to decode an instruction's disassembly to retrieve its human-readable description. Consider the following textual representation of an RDNA 1 instruction: `V_CVT_PK_U8_F32`.
3232

33-
### 1.1 Decoding a single instruction in binary format ###
34-
35-
Consider the following binary representation of an RDNA instruction: `8BEA7E6A`. Let's use the API to decode it.
36-
37-
Step 1: Create a variable to store the binary representation.
33+
### Step 1: Create an empty instance `InstructionInfo`<sup>[Appendix 1]</sup> object.
34+
The `InstructionInfo` struct is the primary structure used by the API to provide information about an instruction. Typically, an empty instance of this struct is passed to the API, which then populates its fields with the relevant data. Before we request information about an instruction, let's create an instance of `InstructionInfo`.
3835
```c
39-
const std::string kStrSampleInstructionBinary = "8BEA7E6A";
36+
amdisa::InstructionInfo instruction_info;
4037
```
4138

42-
Step 2: Convert the binary representation to its decimal equivalent.
43-
44-
We convert the hexadecimal value to its unsigned int (decimal) equivalent with the help of std::stringstream as shown below. This is, of course, only necessary for the sake of this example. In the wild, you will be able to use the API directly on binary machine code obtained from AMD GPU Code Objects.
45-
```c
46-
std::stringstream IsaInstructionStream;
47-
IsaInstructionStream << kStrSampleInstructionBinary;
48-
uint64_t instruction_binary = 0;
49-
IsaInstructionStream >> std::hex >> instruction_binary;
39+
### Step 2: Call the `DecodeInstruction()` API function.
40+
We then proceed to provide the instruction name `V_CVT_PK_U8_F32`, `instruction_info, and `error_msg` to `DecodeInstruction()`. The method returns true on successful decode and false if the decoding fails. The failure reason is populated in the error_msg.
41+
```c
42+
bool is_success = api_decoder.DecodeInstruction("V_CVT_PK_U8_F32",
43+
instruction_info, error_msg);
44+
```
45+
That's it, on a successful decode, we get the following information from instruction_info.
46+
```
47+
Instruction Name: V_CVT_PK_U8_F32
48+
Instruction Description:
49+
Convert a single-precision float value from
50+
the first input to an unsigned 8-bit integer
51+
value and pack the result into one byte of
52+
the third input using the second input as a
53+
byte select. Store the result into a vector register.
5054
```
5155

52-
**Note**: To use std::stringstream, we will have to ```#include <sstream>```
56+
## B. Decoding a single instruction.
57+
Now, let's try decoding a single instruction in machine code format. Consider the following binary representation of an RDNA 3 instruction: `8BEA7E6A`. Let's use the API to decode it.
5358

54-
Step 3: Create an empty `InstructionInfoBundle`<sup>[2]</sup>.
59+
### Step 1: Create an empty `InstructionInfoBundle`<sup>[Appendix 2]</sup>.
60+
To obtain the decoded information, we pass an empty data structure `InstructionInfoBundle`. The `InstructionInfoBundle` is a simple wrapper around the `InstructionInfo` struct that was introduced use case A earlier. The bundle is designed to store multiple `InstructionInfo` instances. This is necessary because, in the RDNA 3 architecture, some instructions are "dual instructions," meaning a single binary-encoded instruction may be decode into two separate instructions. To handle and store information about both instructions, the `InstructionInfoBundle` was introduced.
5561

56-
To obtain the decoded information, we pass an empty data structure `amdisa::InstructionInfoBundle`.
5762
```c
5863
amdisa::InstructionInfoBundle instruction_info_bundle;
5964
```
6065

61-
Step 4: Call the `DecodeInstruction()` API function.
62-
66+
### Step 2: Call the `DecodeInstruction()` API function.
6367
We then proceed to provide the decimal equivalent `instruction_binary`, `instruction_info_bundle`, and `error_msg` to `DecodeInstruction()` which returns true on successful decode and false if the decoding fails.
68+
6469
```c
65-
bool is_success = api_decoder.DecodeInstruction(instruction_binary, instruction_info_bundle, error_msg);
70+
bool is_success = decoder.DecodeInstruction(0x8BEA7E6A,
71+
instruction_info_bundle, error_msg);
72+
```
6673
```
67-
68-
**Note**: It is possible to provide a decimal equivalent directly to the DecodeInstruction() API. Skip 2 & 3.
69-
7074
Output:
7175
Instruction Name: S_AND_B64
7276
Instruction Description: Bitwise AND.
7377
Encoding Name: ENC_SOP2
74-
Encoding Description: SCALAR ALU OPERATIONS WITH ONE DESTINATION AND TWO SOURCES.
78+
Encoding Description:
79+
SCALAR ALU OPERATIONS WITH
80+
ONE DESTINATION AND TWO SOURCES.
7581
ALLOWED PATTERNS:
7682
SOP2 (32 BITS)
7783
SOP2 + LITERAL (64 BITS)
78-
79-
`8BEA7E6A` decodes to a scalar ALU instruction (`S_AND_B64`) that performs bitwise AND operation. The instruction's encoding is `ENC_SOP2` implying a scalar ALU operation with one destination and two sources.
80-
81-
### 1.2 Decoding a single instruction in texual format ###
82-
83-
Now, let's learn how the API can be used to decode an instruction's disassembly to retrieve its human-readable description. Consider the following textual representation of an RDNA instruction: `v_mov_b32`.
84-
85-
Step 1: Create a variable to store the instruction name.
86-
```c
87-
const std::string kStrSampleInstruction = "v_mov_b32";
88-
```
89-
90-
Step 2: Create an empty `InstructionInfo`<sup>[1]</sup> struct variable.
91-
```c
92-
amdisa::InstructionInfo instruction_info;
93-
```
94-
95-
Step 3: Call the `DecodeInstruction()` API function.
96-
97-
We then proceed to provide the instruction name `kStrSampleInstruction`, `instruction_info1, and `error_msg` to `DecodeInstruction()`. Returns true on successful decode and false if the decoding fails. The failure reason is populated in the error_msg.
98-
```c
99-
bool is_success = api_decoder.DecodeInstruction(kStrSampleInstruction, instruction_info, error_msg);
10084
```
85+
`8BEA7E6A` decodes to a scalar ALU instruction (`S_AND_B64`) that performs bitwise AND operation. The instruction's encoding is `ENC_SOP2` implying a scalar ALU operation with one destination and two sources.
10186

102-
Output:
103-
On a successful decode, we get the following information from instruction_info.
104-
105-
Instruction Name: V_MOV_B32
106-
Instruction Description: Move data to a VGPR.
107-
108-
## 2. Decoding a whole shader ##
109-
110-
### 2.1 Decoding a whole shader in binary format ###
11187

88+
## C. Decoding a whole shader
11289
The API accepts and decodes a whole shader in the form of binary stream of instructions. Let's have a look at the following binary stream of instructions:
11390
```
114-
`8BEA7E6A D6130002 00884D02`
91+
8BEA7E6A D6130002 00884D02
11592
```
11693

117-
Step 1: Create a variable to store the sample instructions in binary format.
94+
### Step 1: Store the stream in the vector.
11895
```c
119-
std::string sample_instructions_binary = "8BEA7E6A D6130002 00884D02";
96+
std::vector<uint32_t> inst_stream = {0x8BEA7E6A, 0xD6130002, 0x00884D02};
12097
```
12198
122-
Step 2: Convert the sample instructions to their respective binary format and store in a std::vector\<uint32_t\>. This is, of course, only necessary for the sake of this example. In practice you can use this API call directly on sequences of AMD GPU assembly instructions.
123-
```c
124-
std::vector<uint32_t> sample_instructions;
125-
std::stringstream SampleInstructionStream(sample_instructions_binary);
126-
uint32_t instruction_binary;
127-
while (SampleInstructionStream >> std::hex >> instruction_binary) {
128-
sample_instructions.push_back(instruction_binary);
129-
}
130-
```
99+
### Step 2: Create a vector of `InstructionInfoBundle`<sup>[Appendix 3]</sup>.
100+
Earlier, we demonstrated how to decode a single instruction and store the decoded information in a structure called `InstructionInfoBundle`. Now, we'll decode a vector of instructions, which logically requires a vector of `InstructionInfoBundle`. So, let's create an instance of that.
131101
132-
Step 3: Create a vector of `InstructionInfoBundle`<sup>[3]</sup>.
133-
To obtain the decoded information, we pass an empty vector of `amdisa::InstructionInfoBundle`.
134102
```c
135103
std::vector<amdisa::InstructionInfoBundle> instruction_info_bundle;
136104
```
137105

138-
Step 4: Call the `DecodeInstructionStream()` API
106+
### Step 3: Call the `DecodeInstructionStream()` API.
107+
108+
We then proceed to provide the vector of sample instructions in machine code format `inst_stream`, `instruction_info_bundle`, and `error_msg` to `DecodeInstructionStream()` API.
139109

140-
We then proceed to provide the vector of sample instructions in binary format `SampleInstructions`, `instruction_info_bundle`, and `error_msg` to `DecodeInstructionStream()` API.
141110
```c
142-
bool is_success = api_decoder.DecodeInstructionStream(SampleInstructions, instruction_info_bundle, error_msg);
111+
bool is_success = decoder.DecodeInstructionStream(inst_stream,
112+
instruction_info_bundle, error_msg);
113+
```
114+
The output will contain the following data.
143115
```
144-
145-
Output:
146116
Instruction Name: S_AND_B64
147117
Instruction Description: Bitwise AND.
148118
Encoding Name: ENC_SOP2
149119
150120
Instruction Name: V_FMA_F32
151121
Instruction Description: Fused single precision multiply add.
152122
Encoding Name: ENC_VOP3
153-
123+
```
154124
**Note**: The `V_FMA_F32` instruction is represented using two dwords (2 x 32-bit). Thus, from the given sample instructions of the shader, two instructions are decoded.
155125

156-
### 2.2 Decoding a whole shader in disassembly format ###
157-
158-
The API can also decode a whole shader in AMD's disassembly format. Consider decoding the shader disassembly file "example_shader_disassembly".
159-
160-
Step 1: Create a variable to store the shader disassembly file path.
161-
```c
162-
const std::string kPathToShaderFile = "/path/to/example_shader_disassembly";
163-
```
126+
## D. Decoding a whole shader in disassembly format.
127+
The API can also decode a whole shader in AMD's disassembly format. Decoding an entire shader in disassembly format is conceptually similar to the use case C above; the shader in disassembly format is essentially a stream of instructions. We'll now use an API to decode this shader from a RDNA 3 disassembly file that contains a single shader disassembly.
164128

165-
Step 2: Create a vector of `InstructionInfoBundle`<sup>[3]</sup> variable.
129+
### Step 1: Create a vector of `InstructionInfoBundle`.
166130
To obtain the decoded information, we pass an empty data structure `amdisa::InstructionInfoBundle`.
167131
```c
168-
amdisa::InstructionInfoBundle instruction_info_stream;
132+
std::vector<amdisa::InstructionInfoBundle> instruction_info_stream;
169133
```
170134

171-
Step 3: Call the DecodeInstruction() API function.
135+
### Step 2: Call the `DecodeShaderDisassemblyFile()` API.
172136

173-
We then proceed to provide the path to the shader disassembly file, `instruction_info_strea`, `error_msg`, and `resolve_direct_branch_targets` to `DecodeShaderText()`.
137+
We then proceed to provide the path to the shader disassembly file, `instruction_info_stream`, `error_msg`, and `resolve_direct_branch_targets` to `DecodeShaderText()`.
174138
```c
175-
bool is_success = api_decoder.DecodeShaderText(kPathToShaderFile, instruction_info_stream, error_msg, resolve_direct_branch_targets);
139+
const char* kPathToShaderFile = "path/to/file";
140+
bool is_success = decoder.DecodeShaderDisassemblyFile(kPathToShaderFile,
141+
instruction_info_stream, error_msg, resolve_direct_branch_targets);
176142
```
177143

178144
**Note**: `resolve_direct_branch_targets` is an optional parameter. Setting it to `true` enables determining the direct branch's target instruction's index in the `instruction_info_bundle` vector which will be available in the `InstructionInfo.instruction_semantic_info.branch_info` struct's member `branch_target_index`. For example, in the following shader:
179145

180-
s_branch label_05F4 // 000000000000: BFA00004
146+
s_branch label_05F4 // 000000000000: BFA00004
181147
label_05E4:
182-
v_mov_b32 v28, 0 // 000000000004: 7E380280
148+
v_mov_b32 v28, 0 // 000000000004: 7E380280
183149
label_05F4:
184-
v_fma_f32 v0, s10, s10, v0 // 000000000008: D6130000 0400140A
150+
v_fma_f32 v0, s10, s10, v0 // 000000000008: D6130000 0400140A
185151

186152
The target instruction index of the s_branch instruction will be 2, i.e, the third instruction v_fma_f32. Indexing in the instruction_info_bundle starts from 0.
187153

@@ -271,4 +237,4 @@ This output data structure is used when we decode the binary representation of t
271237

272238
### 3. A vector of amdisa::InstructionInfoBundle ###
273239

274-
This output vector is used when a stream of instructions is provided to decode. On a successful decode, the decoded instruction infos are populated in this vector in the same order as provided. The first instruction's info index in the vector is 0.
240+
This output vector is used when a stream of instructions is provided to decode. On a successful decode, the decoded instruction infos are populated in this vector in the same order as provided. The first instruction's info index in the vector is 0.

documentation/cli_documentation.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ ISA Spec Manager CLI program is an example program that leverages IsaSpecApi. Th
55
To decode an instruction that is represented in the binary (machine code) format using CLI, run the commands below.
66

77
```bash
8-
cd IsaSpecManager-Win64/cli
9-
./IsaSpecCli.exe -x ../../Specification/xml/gfx11.xml -d BE804814
8+
cd isa_spec_manager-Win64/cli
9+
./isa_spec_cli.exe -x ../../Specification/xml/gfx11.xml -d BE804814
1010
```
1111
As can be seen from the command, `-x` flag should be followed by the path to the machine-readable XML specification. Next, `-d` flag should be followed by the instruction in the binary form. Note that the provided instruction should be encoded in the specified architecture. After running the command, the CLI outputs the following:
1212

@@ -33,8 +33,8 @@ If decoded successfully, CLI outputs the instruction in an assembly form, descri
3333
To retrieve information about a specific instruction from the spec, the name of the instruction could be provided to the CLI. Refer to the commands below.
3434

3535
```bash
36-
cd IsaSpecManager-Win64/cli
37-
./IsaSpecCli.exe -x ../../Specification/xml/gfx11.xml -i s_setpc_b64
36+
cd isa_spec_manager-Win64/cli
37+
./isa_spec_cli.exe -x ../../Specification/xml/gfx11.xml -i s_setpc_b64
3838
```
3939
As can be seen from the command, `-x` flag should be followed by the path to the machine-readable XML specification. Next, `-i` flag should be followed by the name of the instruction which we are interested in. Note, that the requested instruction should be present in the specified architecture. After running the command, CLI outputs the following:
4040

0 commit comments

Comments
 (0)