[llvm] [Utils][SPIR-V] Adding spirv-sim to LLVM (PR #104020)
Ilia Diachkov via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 26 05:07:05 PDT 2024
Nathan =?utf-8?q?Gauër?= <brioche at google.com>,
Nathan =?utf-8?q?Gauër?= <brioche at google.com>,
Nathan =?utf-8?q?Gauër?= <brioche at google.com>,
Nathan =?utf-8?q?Gauër?= <brioche at google.com>,
Nathan =?utf-8?q?Gauër?= <brioche at google.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/104020 at github.com>
================
@@ -0,0 +1,658 @@
+#!/usr/bin/env python3
+
+from __future__ import annotations
+from dataclasses import dataclass
+from instructions import *
+from typing import Any, Iterable, Callable, Optional, Tuple
+import argparse
+import fileinput
+import inspect
+import re
+import sys
+
+RE_EXPECTS = re.compile(r"^([0-9]+,)*[0-9]+$")
+
+
+# Parse the SPIR-V instructions. Some instructions are ignored because
+# not required to simulate this module.
+# Instructions are to be implemented in instructions.py
+def parseInstruction(i):
+ IGNORED = set(
+ [
+ "OpCapability",
+ "OpMemoryModel",
+ "OpExecutionMode",
+ "OpExtension",
+ "OpSource",
+ "OpTypeInt",
+ "OpTypeStruct",
+ "OpTypeFloat",
+ "OpTypeBool",
+ "OpTypeVoid",
+ "OpTypeFunction",
+ "OpTypePointer",
+ "OpTypeArray",
+ ]
+ )
+ if i.opcode() in IGNORED:
+ return None
+
+ try:
+ Type = getattr(sys.modules["instructions"], i.opcode())
+ except AttributeError:
+ raise RuntimeError(f"Unsupported instruction {i}")
+ if not inspect.isclass(Type):
+ raise RuntimeError(
+ f"{i} instruction definition is not a class. Did you used 'def' instead of 'class'?"
+ )
+ return Type(i.line)
+
+
+# Split a list of instructions into pieces. Pieces are delimited by instructions of the type splitType.
+# The delimiter is the first instruction of the next piece.
+# This function returns no empty pieces:
+# - if 2 subsequent delimiters will mean 2 pieces. One with only the first delimiter, and the second
+# with the delimiter and following instructions.
+# - if the first instruction is a delimiter, the first piece will begin with this delimiter.
+def splitInstructions(
+ splitType: type, instructions: Iterable[Instruction]
+) -> list[list[Instruction]]:
+ blocks: list[list[Instruction]] = [[]]
+ for instruction in instructions:
+ if isinstance(instruction, splitType) and len(blocks[-1]) > 0:
+ blocks.append([])
+ blocks[-1].append(instruction)
+ return blocks
+
+
+# Defines a BasicBlock in the simulator.
+# Begins at an OpLabel, and ends with a control-flow instruction.
+class BasicBlock:
+ def __init__(self, instructions) -> None:
+ assert isinstance(instructions[0], OpLabel)
+ # The name of the basic block, which is the register of the leading
+ # OpLabel.
+ self._name = instructions[0].output_register()
+ # The list of instructions belonging to this block.
+ self._instructions = instructions[1:]
+
+ # Returns the name of this basic block.
+ def name(self):
+ return self._name
+
+ # Returns the instruction at index in this basic block.
+ def __getitem__(self, index: int) -> Instruction:
+ return self._instructions[index]
+
+ # Returns the number of instructions in this basic block, excluding the
+ # leading OpLabel.
+ def __len__(self):
+ return len(self._instructions)
+
+ def dump(self):
+ print(f" {self._name}:")
+ for instruction in self._instructions:
+ print(f" {instruction}")
+
+
+# Defines a Function in the simulator.
+class Function:
+ def __init__(self, instructions) -> None:
+ assert isinstance(instructions[0], OpFunction)
+ # The name of the function (name of the register returned by OpFunction).
+ self._name: str = instructions[0].output_register()
+ # The list of basic blocks that belongs to this function.
+ self._basic_blocks: list[BasicBlock] = []
+ # The variables local to this function.
+ self._variables: list[OpVariable] = [
+ x for x in instructions if isinstance(x, OpVariable)
+ ]
+
+ assert isinstance(instructions[-1], OpFunctionEnd)
+ body = filter(lambda x: not isinstance(x, OpVariable), instructions[1:-1])
+ for block in splitInstructions(OpLabel, body):
+ self._basic_blocks.append(BasicBlock(block))
+
+ # Returns the name of this function.
+ def name(self) -> str:
+ return self._name
+
+ # Returns the basic block at index in this function.
+ def __getitem__(self, index: int) -> BasicBlock:
+ return self._basic_blocks[index]
+
+ # Returns the index of the basic block with the given name if found,
+ # -1 otherwise.
+ def get_bb_index(self, name) -> int:
+ for i in range(len(self._basic_blocks)):
+ if self._basic_blocks[i].name() == name:
+ return i
+ return -1
+
+ def dump(self):
+ print(" Variables:")
+ for var in self._variables:
+ print(f" {var}")
+ print(" Blocks:")
+ for bb in self._basic_blocks:
+ bb.dump()
+
+
+# Represents an instruction pointer in the simulator.
+ at dataclass
+class InstructionPointer:
+ # The current function the IP points to.
+ function: Function
+ # The basic block index in function IP points to.
+ basic_block: int
+ # The instruction in basic_block IP points to.
+ instruction_index: int
+
+ def __str__(self):
+ bb = self.function[self.basic_block]
+ i = bb[self.instruction_index]
+ return f"{bb.name()}:{self.instruction_index} in {self.function.name()} | {i}"
+
+ def __hash__(self):
+ return hash((self.function.name(), self.basic_block, self.instruction_index))
+
+ # Returns the basic block IP points to.
+ def bb(self) -> BasicBlock:
+ return self.function[self.basic_block]
+
+ # Returns the instruction IP points to.
+ def instruction(self):
+ return self.function[self.basic_block][self.instruction_index]
+
+ # Increment IP by 1. This only works inside a basic-block boundary.
+ # Incrementing IP when at the boundary of a basic block will fail.
+ def __add__(self, value: int):
+ bb = self.function[self.basic_block]
+ assert len(bb) > self.instruction_index + value
+ return InstructionPointer(
+ self.function, self.basic_block, self.instruction_index + value
+ )
+
+
+# Defines a Lane in this simulator.
+class Lane:
+ # The registers known by this lane.
+ _registers: dict[str, Any]
+ # The current IP of this lane.
+ _ip: Optional[InstructionPointer]
+ # If this lane running.
+ _running: bool
+ # The wave this lane belongs to.
+ _wave: Wave
+ # The callstack of this lane. Each tuple represents 1 call.
+ # The first element is the IP the function will return to.
+ # The second element is the callback to call to store the return value
+ # into the correct register.
----------------
iliya-diyachkov wrote:
Correct indentation?
https://github.com/llvm/llvm-project/pull/104020
More information about the llvm-commits
mailing list