First Commit
This commit is contained in:
249
externals/nihstro/include/nihstro/bit_field.h
vendored
Normal file
249
externals/nihstro/include/nihstro/bit_field.h
vendored
Normal file
@@ -0,0 +1,249 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#ifndef __forceinline
|
||||
#ifndef _WIN32
|
||||
#define __forceinline inline __attribute__((always_inline))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace nihstro {
|
||||
|
||||
/*
|
||||
* Abstract bitfield class
|
||||
*
|
||||
* Allows endianness-independent access to individual bitfields within some raw
|
||||
* integer value. The assembly generated by this class is identical to the
|
||||
* usage of raw bitfields, so it's a perfectly fine replacement.
|
||||
*
|
||||
* For BitField<X,Y,Z>, X is the distance of the bitfield to the LSB of the
|
||||
* raw value, Y is the length in bits of the bitfield. Z is an integer type
|
||||
* which determines the sign of the bitfield. Z must have the same size as the
|
||||
* raw integer.
|
||||
*
|
||||
*
|
||||
* General usage:
|
||||
*
|
||||
* Create a new union with the raw integer value as a member.
|
||||
* Then for each bitfield you want to expose, add a BitField member
|
||||
* in the union. The template parameters are the bit offset and the number
|
||||
* of desired bits.
|
||||
*
|
||||
* Changes in the bitfield members will then get reflected in the raw integer
|
||||
* value and vice-versa.
|
||||
*
|
||||
*
|
||||
* Sample usage:
|
||||
*
|
||||
* union SomeRegister
|
||||
* {
|
||||
* u32 hex;
|
||||
*
|
||||
* BitField<0,7,u32> first_seven_bits; // unsigned
|
||||
* BitField<7,8,u32> next_eight_bits; // unsigned
|
||||
* BitField<3,15,s32> some_signed_fields; // signed
|
||||
* };
|
||||
*
|
||||
* This is equivalent to the little-endian specific code:
|
||||
*
|
||||
* union SomeRegister
|
||||
* {
|
||||
* u32 hex;
|
||||
*
|
||||
* struct
|
||||
* {
|
||||
* u32 first_seven_bits : 7;
|
||||
* u32 next_eight_bits : 8;
|
||||
* };
|
||||
* struct
|
||||
* {
|
||||
* u32 : 3; // padding
|
||||
* s32 some_signed_fields : 15;
|
||||
* };
|
||||
* };
|
||||
*
|
||||
*
|
||||
* Caveats:
|
||||
*
|
||||
* 1)
|
||||
* BitField provides automatic casting from and to the storage type where
|
||||
* appropriate. However, when using non-typesafe functions like printf, an
|
||||
* explicit cast must be performed on the BitField object to make sure it gets
|
||||
* passed correctly, e.g.:
|
||||
* printf("Value: %d", (s32)some_register.some_signed_fields);
|
||||
*
|
||||
* 2)
|
||||
* Not really a caveat, but potentially irritating: This class is used in some
|
||||
* packed structures that do not guarantee proper alignment. Therefore we have
|
||||
* to use #pragma pack here not to pack the members of the class, but instead
|
||||
* to break GCC's assumption that the members of the class are aligned on
|
||||
* sizeof(StorageType).
|
||||
* TODO(neobrain): Confirm that this is a proper fix and not just masking
|
||||
* symptoms.
|
||||
*/
|
||||
#pragma pack(1)
|
||||
template<std::size_t position, std::size_t bits, typename T>
|
||||
struct BitField
|
||||
{
|
||||
private:
|
||||
// This constructor might be considered ambiguous:
|
||||
// Would it initialize the storage or just the bitfield?
|
||||
// Hence, delete it. Use the assignment operator to set bitfield values!
|
||||
BitField(T val) = delete;
|
||||
|
||||
public:
|
||||
// Force default constructor to be created
|
||||
// so that we can use this within unions
|
||||
BitField() = default;
|
||||
|
||||
#ifndef _WIN32
|
||||
// We explicitly delete the copy assigment operator here, because the
|
||||
// default copy assignment would copy the full storage value, rather than
|
||||
// just the bits relevant to this particular bit field.
|
||||
// Ideally, we would just implement the copy assignment to copy only the
|
||||
// relevant bits, but this requires compiler support for unrestricted
|
||||
// unions.
|
||||
// MSVC 2013 has no support for this, hence we disable this code on
|
||||
// Windows (so that the default copy assignment operator will be used).
|
||||
// For any C++11 conformant compiler we delete the operator to make sure
|
||||
// we never use this inappropriate operator to begin with.
|
||||
// TODO: Implement this operator properly once all target compilers
|
||||
// support unrestricted unions.
|
||||
// TODO: Actually, deleting and overriding this operator both cause more
|
||||
// harm than anything. Instead, it's suggested to never use the copy
|
||||
// constructor directly but instead invoke Assign() explicitly.
|
||||
// BitField& operator=(const BitField&) = delete;
|
||||
#endif
|
||||
|
||||
__forceinline BitField& operator=(T val)
|
||||
{
|
||||
Assign(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
__forceinline operator typename std::add_const<T>::type() const
|
||||
{
|
||||
return Value();
|
||||
}
|
||||
|
||||
__forceinline void Assign(const T& value) {
|
||||
storage = (storage & ~GetMask()) | ((((StorageType)value) << position) & GetMask());
|
||||
}
|
||||
|
||||
__forceinline typename std::add_const<T>::type Value() const
|
||||
{
|
||||
if (std::numeric_limits<T>::is_signed)
|
||||
{
|
||||
std::size_t shift = 8 * sizeof(T)-bits;
|
||||
return (T)(((storage & GetMask()) << (shift - position)) >> shift);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (T)((storage & GetMask()) >> position);
|
||||
}
|
||||
}
|
||||
|
||||
static size_t NumBits() {
|
||||
return bits;
|
||||
}
|
||||
|
||||
private:
|
||||
// StorageType is T for non-enum types and the underlying type of T if
|
||||
// T is an enumeration. Note that T is wrapped within an enable_if in the
|
||||
// former case to workaround compile errors which arise when using
|
||||
// std::underlying_type<T>::type directly.
|
||||
typedef typename std::conditional < std::is_enum<T>::value,
|
||||
std::underlying_type<T>,
|
||||
std::enable_if < true, T >> ::type::type StorageType;
|
||||
|
||||
// Unsigned version of StorageType
|
||||
typedef typename std::make_unsigned<StorageType>::type StorageTypeU;
|
||||
|
||||
__forceinline StorageType GetMask() const
|
||||
{
|
||||
return ((~(StorageTypeU)0) >> (8 * sizeof(T)-bits)) << position;
|
||||
}
|
||||
|
||||
StorageType storage;
|
||||
|
||||
static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
|
||||
|
||||
// And, you know, just in case people specify something stupid like bits=position=0x80000000
|
||||
static_assert(position < 8 * sizeof(T), "Invalid position");
|
||||
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
|
||||
static_assert(bits > 0, "Invalid number of bits");
|
||||
static_assert(std::is_standard_layout<T>::value, "Invalid base type");
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract bit flag class. This is basically a specialization of BitField for single-bit fields.
|
||||
* Instead of being cast to the underlying type, it acts like a boolean.
|
||||
*/
|
||||
template<std::size_t position, typename T>
|
||||
struct BitFlag : protected BitField<position, 1, T>
|
||||
{
|
||||
private:
|
||||
BitFlag(T val) = delete;
|
||||
|
||||
typedef BitField<position, 1, T> ParentType;
|
||||
|
||||
public:
|
||||
BitFlag() = default;
|
||||
|
||||
#ifndef _WIN32
|
||||
BitFlag& operator=(const BitFlag&) = delete;
|
||||
#endif
|
||||
|
||||
__forceinline BitFlag& operator=(bool val)
|
||||
{
|
||||
Assign(val);
|
||||
return *this;
|
||||
}
|
||||
|
||||
__forceinline operator bool() const
|
||||
{
|
||||
return Value();
|
||||
}
|
||||
|
||||
__forceinline void Assign(bool value) {
|
||||
ParentType::Assign(value);
|
||||
}
|
||||
|
||||
__forceinline bool Value() const
|
||||
{
|
||||
return ParentType::Value() != 0;
|
||||
}
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
} // namespace
|
||||
48
externals/nihstro/include/nihstro/float24.h
vendored
Normal file
48
externals/nihstro/include/nihstro/float24.h
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
||||
#include "bit_field.h"
|
||||
|
||||
namespace nihstro {
|
||||
|
||||
inline uint32_t to_float24(float val) {
|
||||
static_assert(std::numeric_limits<float>::is_iec559, "Compiler does not adhere to IEEE 754");
|
||||
|
||||
union Float32 {
|
||||
BitField< 0, 23, uint32_t> mant;
|
||||
BitField<23, 8, uint32_t> biased_exp;
|
||||
BitField<31, 1, uint32_t> sign;
|
||||
|
||||
static int ExponentBias() {
|
||||
return 127;
|
||||
}
|
||||
} f32 = reinterpret_cast<Float32&>(val);
|
||||
|
||||
union Float24 {
|
||||
uint32_t hex;
|
||||
|
||||
BitField< 0, 16, uint32_t> mant;
|
||||
BitField<16, 7, uint32_t> biased_exp;
|
||||
BitField<23, 1, uint32_t> sign;
|
||||
|
||||
static int ExponentBias() {
|
||||
return 63;
|
||||
}
|
||||
} f24 = { 0 };
|
||||
|
||||
int biased_exp = (int)f32.biased_exp - Float32::ExponentBias() + Float24::ExponentBias();
|
||||
unsigned mant = (biased_exp >= 0) ? (f32.mant >> (f32.mant.NumBits() - f24.mant.NumBits())) : 0;
|
||||
if (biased_exp >= (1 << f24.biased_exp.NumBits())) {
|
||||
// TODO: Return +inf or -inf
|
||||
}
|
||||
|
||||
f24.biased_exp = std::max(0, biased_exp);
|
||||
f24.mant = mant;
|
||||
f24.sign = f32.sign.Value();
|
||||
|
||||
return f24.hex;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
577
externals/nihstro/include/nihstro/inline_assembly.h
vendored
Normal file
577
externals/nihstro/include/nihstro/inline_assembly.h
vendored
Normal file
@@ -0,0 +1,577 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <initializer_list>
|
||||
#include <vector>
|
||||
|
||||
#include "bit_field.h"
|
||||
#include "float24.h"
|
||||
#include "shader_binary.h"
|
||||
#include "shader_bytecode.h"
|
||||
|
||||
namespace nihstro {
|
||||
|
||||
struct ShaderBinary {
|
||||
std::vector<Instruction> program;
|
||||
std::vector<SwizzlePattern> swizzle_table;
|
||||
std::vector<OutputRegisterInfo> output_table;
|
||||
std::vector<ConstantInfo> constant_table;
|
||||
std::vector<char> symbol_table;
|
||||
std::vector<UniformInfo> uniform_table;
|
||||
};
|
||||
|
||||
struct DestRegisterOrTemporary : public DestRegister {
|
||||
DestRegisterOrTemporary(const DestRegister& oth) : DestRegister(oth) {}
|
||||
DestRegisterOrTemporary(const SourceRegister& oth) : DestRegister(DestRegister::FromTypeAndIndex(oth.GetRegisterType(), oth.GetIndex())) {
|
||||
if (oth.GetRegisterType() != RegisterType::Temporary)
|
||||
throw "Invalid source register used as output";
|
||||
}
|
||||
};
|
||||
|
||||
struct InlineAsm {
|
||||
enum RelativeAddress {
|
||||
None,
|
||||
A1,
|
||||
A2,
|
||||
AL
|
||||
};
|
||||
|
||||
struct DestMask {
|
||||
DestMask(const std::string& mask) {
|
||||
static const std::map<std::string,uint32_t> valid_masks {
|
||||
{ "x", 8 }, { "y", 4 }, { "z", 2 }, { "w", 1 },
|
||||
{ "xy", 12 }, { "xz", 10 }, { "xw", 9 },
|
||||
{ "yz", 6 }, { "yw", 5 }, { "zw", 3 },
|
||||
{ "xyz", 14 }, { "xyw", 13 }, { "xzw", 11 }, { "yzw", 7 },
|
||||
{ "xyzw", 15 }, { "", 15 }
|
||||
};
|
||||
|
||||
dest_mask = valid_masks.at(mask);
|
||||
}
|
||||
|
||||
DestMask(const char* mask) : DestMask(std::string(mask)) {}
|
||||
|
||||
DestMask(const DestMask&) = default;
|
||||
|
||||
uint32_t dest_mask;
|
||||
};
|
||||
|
||||
struct SwizzleMask {
|
||||
SwizzleMask(const std::string& swizzle) : negate(false) {
|
||||
selectors[0] = SwizzlePattern::Selector::x;
|
||||
selectors[1] = SwizzlePattern::Selector::y;
|
||||
selectors[2] = SwizzlePattern::Selector::z;
|
||||
selectors[3] = SwizzlePattern::Selector::w;
|
||||
|
||||
if (swizzle.length() == 0)
|
||||
return;
|
||||
|
||||
if (swizzle.length() > 5) {
|
||||
throw "Invalid swizzle mask";
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
|
||||
if (swizzle[index] == '-') {
|
||||
negate = true;
|
||||
} else if (swizzle[index] == '+') {
|
||||
index++;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (swizzle.length() <= index + i)
|
||||
return;
|
||||
|
||||
switch (swizzle[index + i]) {
|
||||
case 'x': selectors[i] = SwizzlePattern::Selector::x; break;
|
||||
case 'y': selectors[i] = SwizzlePattern::Selector::y; break;
|
||||
case 'z': selectors[i] = SwizzlePattern::Selector::z; break;
|
||||
case 'w': selectors[i] = SwizzlePattern::Selector::w; break;
|
||||
default:
|
||||
throw "Invalid swizzle mask";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SwizzleMask(const char* swizzle) : SwizzleMask(std::string(swizzle)) {}
|
||||
|
||||
SwizzleMask(const SwizzleMask&) = default;
|
||||
|
||||
SwizzlePattern::Selector selectors[4];
|
||||
bool negate;
|
||||
};
|
||||
|
||||
enum Type {
|
||||
Regular,
|
||||
Output,
|
||||
Constant,
|
||||
Uniform,
|
||||
Else,
|
||||
EndIf,
|
||||
EndLoop,
|
||||
Label
|
||||
} type;
|
||||
|
||||
InlineAsm(Type type) : type(type) {
|
||||
}
|
||||
|
||||
InlineAsm(OpCode opcode) : type(Regular) {
|
||||
if (opcode.GetInfo().type != OpCode::Type::Trivial) {
|
||||
throw "Invalid opcode used with zero arguments";
|
||||
}
|
||||
full_instruction.instr.opcode = opcode;
|
||||
}
|
||||
|
||||
InlineAsm(OpCode opcode, int src) : type(Regular) {
|
||||
switch (opcode.EffectiveOpCode()) {
|
||||
case OpCode::Id::LOOP:
|
||||
// if (src.GetRegisterType() != RegisterType::IntUniform)
|
||||
// throw "LOOP argument must be an integer register!";
|
||||
|
||||
reg_id = src;
|
||||
full_instruction.instr.hex = 0;
|
||||
full_instruction.instr.opcode = opcode;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw "Unknown opcode argument";
|
||||
}
|
||||
}
|
||||
|
||||
InlineAsm(OpCode opcode, DestRegisterOrTemporary dest, const DestMask& dest_mask,
|
||||
SourceRegister src1, const SwizzleMask swizzle_src1 = SwizzleMask{""}) : type(Regular) {
|
||||
Instruction& instr = full_instruction.instr;
|
||||
instr.hex = 0;
|
||||
instr.opcode = opcode;
|
||||
SwizzlePattern& swizzle = full_instruction.swizzle;
|
||||
swizzle.hex = 0;
|
||||
|
||||
switch(opcode.GetInfo().type) {
|
||||
case OpCode::Type::Arithmetic:
|
||||
// TODO: Assert valid inputs, considering the field width!
|
||||
instr.common.dest = dest;
|
||||
instr.common.src1 = src1;
|
||||
swizzle.negate_src1 = swizzle_src1.negate;
|
||||
|
||||
swizzle.dest_mask = dest_mask.dest_mask;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
swizzle.SetSelectorSrc1(i, swizzle_src1.selectors[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw "Unknown inline assmembler command";
|
||||
}
|
||||
}
|
||||
|
||||
InlineAsm(OpCode opcode, DestRegisterOrTemporary dest, const DestMask& dest_mask,
|
||||
SourceRegister src1, const SwizzleMask& swizzle_src1,
|
||||
SourceRegister src2, const SwizzleMask& swizzle_src2 = "", RelativeAddress addr = None) : type(Regular) {
|
||||
Instruction& instr = full_instruction.instr;
|
||||
instr.hex = 0;
|
||||
instr.opcode = opcode;
|
||||
SwizzlePattern& swizzle = full_instruction.swizzle;
|
||||
swizzle.hex = 0;
|
||||
|
||||
switch(opcode.GetInfo().type) {
|
||||
case OpCode::Type::Arithmetic:
|
||||
// TODO: Assert valid inputs, considering the field width!
|
||||
instr.common.dest = dest;
|
||||
instr.common.src1 = src1;
|
||||
instr.common.src2 = src2;
|
||||
instr.common.address_register_index = addr;
|
||||
swizzle.negate_src1 = swizzle_src1.negate;
|
||||
swizzle.negate_src2 = swizzle_src2.negate;
|
||||
|
||||
swizzle.dest_mask = dest_mask.dest_mask;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
swizzle.SetSelectorSrc1(i, swizzle_src1.selectors[i]);
|
||||
swizzle.SetSelectorSrc2(i, swizzle_src2.selectors[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw "Unknown inline assembler command";
|
||||
}
|
||||
}
|
||||
|
||||
InlineAsm(OpCode opcode, DestRegisterOrTemporary dest, const DestMask& dest_mask,
|
||||
SourceRegister src1, const SwizzleMask& swizzle_src1,
|
||||
SourceRegister src2, const SwizzleMask& swizzle_src2,
|
||||
SourceRegister src3, const SwizzleMask& swizzle_src3 = {""}) : type(Regular) {
|
||||
Instruction& instr = full_instruction.instr;
|
||||
instr.hex = 0;
|
||||
instr.opcode = opcode;
|
||||
SwizzlePattern& swizzle = full_instruction.swizzle;
|
||||
swizzle.hex = 0;
|
||||
|
||||
switch(opcode.GetInfo().type) {
|
||||
case OpCode::Type::MultiplyAdd:
|
||||
// TODO: Assert valid inputs, considering the field width!
|
||||
instr.mad.dest = dest;
|
||||
instr.mad.src1 = src1;
|
||||
instr.mad.src2 = src2;
|
||||
instr.mad.src3 = src3;
|
||||
full_instruction.swizzle.negate_src1 = swizzle_src1.negate;
|
||||
full_instruction.swizzle.negate_src2 = swizzle_src2.negate;
|
||||
full_instruction.swizzle.negate_src3 = swizzle_src3.negate;
|
||||
|
||||
swizzle.dest_mask = dest_mask.dest_mask;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
full_instruction.swizzle.SetSelectorSrc1(i, swizzle_src1.selectors[i]);
|
||||
full_instruction.swizzle.SetSelectorSrc2(i, swizzle_src2.selectors[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw "Unknown inline assembler command";
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience constructors with implicit swizzle mask
|
||||
InlineAsm(OpCode opcode, DestRegisterOrTemporary dest,
|
||||
SourceRegister src1, const SwizzleMask& swizzle_src1 = "") : InlineAsm(opcode, dest, "", src1, swizzle_src1) {}
|
||||
InlineAsm(OpCode opcode, DestRegisterOrTemporary dest,
|
||||
SourceRegister src1, const SwizzleMask& swizzle_src1,
|
||||
SourceRegister src2, const SwizzleMask& swizzle_src2 = "") : InlineAsm(opcode, dest, "", src1, swizzle_src1, src2, swizzle_src2) {}
|
||||
InlineAsm(OpCode opcode, DestRegisterOrTemporary dest, const DestMask& dest_mask,
|
||||
SourceRegister src1,
|
||||
SourceRegister src2, const SwizzleMask& swizzle_src2 = "") : InlineAsm(opcode, dest, dest_mask, src1, "", src2, swizzle_src2) {}
|
||||
InlineAsm(OpCode opcode, DestRegisterOrTemporary dest,
|
||||
SourceRegister src1,
|
||||
SourceRegister src2, const SwizzleMask& swizzle_src2 = "") : InlineAsm(opcode, dest, "", src1, "", src2, swizzle_src2) {}
|
||||
InlineAsm(OpCode opcode, DestRegisterOrTemporary dest,
|
||||
SourceRegister src1, const SwizzleMask& swizzle_src1,
|
||||
SourceRegister src2, const SwizzleMask& swizzle_src2,
|
||||
SourceRegister src3, const SwizzleMask& swizzle_src3 = "") : InlineAsm(opcode, dest, "", src1, swizzle_src1, src2, swizzle_src2, src3, swizzle_src3) {}
|
||||
InlineAsm(OpCode opcode, DestRegisterOrTemporary dest, const DestMask& dest_mask,
|
||||
SourceRegister src1,
|
||||
SourceRegister src2, const SwizzleMask& swizzle_src2,
|
||||
SourceRegister src3, const SwizzleMask& swizzle_src3 = "") : InlineAsm(opcode, dest, dest_mask, src1, "", src2, swizzle_src2, src3, swizzle_src3) {}
|
||||
InlineAsm(OpCode opcode, DestRegisterOrTemporary dest, const DestMask& dest_mask,
|
||||
SourceRegister src1, const SwizzleMask& swizzle_src1,
|
||||
SourceRegister src2,
|
||||
SourceRegister src3, const SwizzleMask& swizzle_src3 = "") : InlineAsm(opcode, dest, dest_mask, src1, swizzle_src1, src2, "", src3, swizzle_src3) {}
|
||||
InlineAsm(OpCode opcode, DestRegisterOrTemporary dest,
|
||||
SourceRegister src1,
|
||||
SourceRegister src2, const SwizzleMask& swizzle_src2,
|
||||
SourceRegister src3, const SwizzleMask& swizzle_src3 = "") : InlineAsm(opcode, dest, "", src1, "", src2, swizzle_src2, src3, swizzle_src3) {}
|
||||
InlineAsm(OpCode opcode, DestRegisterOrTemporary dest,
|
||||
SourceRegister src1, const SwizzleMask& swizzle_src1,
|
||||
SourceRegister src2,
|
||||
SourceRegister src3, const SwizzleMask& swizzle_src3 = "") : InlineAsm(opcode, dest, "", src1, swizzle_src1, src2, "", src3, swizzle_src3) {}
|
||||
InlineAsm(OpCode opcode, DestRegisterOrTemporary dest, const DestMask& dest_mask,
|
||||
SourceRegister src1,
|
||||
SourceRegister src2,
|
||||
SourceRegister src3, const SwizzleMask& swizzle_src3 = "") : InlineAsm(opcode, dest, dest_mask, src1, "", src2, "", src3, swizzle_src3) {}
|
||||
InlineAsm(OpCode opcode, DestRegisterOrTemporary dest,
|
||||
SourceRegister src1,
|
||||
SourceRegister src2,
|
||||
SourceRegister src3, const SwizzleMask& swizzle_src3 = "") : InlineAsm(opcode, dest, "", src1, "", src2, "", src3, swizzle_src3) {}
|
||||
|
||||
static InlineAsm DeclareOutput(DestRegister reg, OutputRegisterInfo::Type semantic) {
|
||||
if (reg.GetRegisterType() != RegisterType::Output)
|
||||
throw "Invalid register used to declare output";
|
||||
|
||||
InlineAsm ret(Output);
|
||||
ret.output_semantic = semantic;
|
||||
ret.reg_id = reg.GetIndex();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static InlineAsm DeclareConstant(SourceRegister reg, float x, float y, float z, float w) {
|
||||
if (reg.GetRegisterType() != RegisterType::FloatUniform)
|
||||
throw "Invalid source register used to declare shader constant";
|
||||
|
||||
InlineAsm ret(Constant);
|
||||
ret.value[0] = x;
|
||||
ret.value[1] = y;
|
||||
ret.value[2] = z;
|
||||
ret.value[3] = w;
|
||||
ret.constant_type = ConstantInfo::Float;
|
||||
ret.reg_id = reg.GetIndex();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static InlineAsm DeclareUniform(SourceRegister reg_first, SourceRegister reg_last, const std::string& name) {
|
||||
InlineAsm ret(Uniform);
|
||||
ret.reg_id = reg_first.GetIndex();
|
||||
ret.reg_id_last = reg_last.GetIndex();
|
||||
ret.name = name;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// TODO: Group this into a union once MSVC supports unrestricted unions!
|
||||
struct {
|
||||
Instruction instr;
|
||||
SwizzlePattern swizzle;
|
||||
} full_instruction;
|
||||
|
||||
std::string name;
|
||||
|
||||
unsigned reg_id;
|
||||
unsigned reg_id_last;
|
||||
|
||||
OutputRegisterInfo::Type output_semantic;
|
||||
|
||||
ConstantInfo::Type constant_type;
|
||||
float value[4];
|
||||
|
||||
static size_t FindSwizzlePattern(const SwizzlePattern& pattern, std::vector<SwizzlePattern>& swizzle_table) {
|
||||
auto it = std::find_if(swizzle_table.begin(), swizzle_table.end(), [&](const SwizzlePattern& candidate) { return candidate.hex == pattern.hex; });
|
||||
size_t ret = std::distance(swizzle_table.begin(), it);
|
||||
|
||||
if (it == swizzle_table.end())
|
||||
swizzle_table.push_back(pattern);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const ShaderBinary CompileToRawBinary(std::initializer_list<InlineAsm> code_) {
|
||||
ShaderBinary binary;
|
||||
std::vector<InlineAsm> code(code_);
|
||||
for (int i = 0; i < code.size(); ++i) {
|
||||
auto command = code[i];
|
||||
|
||||
switch (command.type) {
|
||||
case Regular:
|
||||
{
|
||||
auto& instr = command.full_instruction.instr;
|
||||
|
||||
switch (instr.opcode.Value().GetInfo().type) {
|
||||
case OpCode::Type::Trivial:
|
||||
break;
|
||||
|
||||
case OpCode::Type::Arithmetic:
|
||||
instr.common.operand_desc_id = FindSwizzlePattern(command.full_instruction.swizzle, binary.swizzle_table);
|
||||
break;
|
||||
|
||||
case OpCode::Type::UniformFlowControl:
|
||||
if (instr.opcode.Value().EffectiveOpCode() == OpCode::Id::LOOP) {
|
||||
instr.flow_control.int_uniform_id = command.reg_id;
|
||||
instr.flow_control.dest_offset = binary.program.size();
|
||||
|
||||
// std::cout << "Started at "<< binary.program.size() << " <-> "<<i <<std::endl;
|
||||
// TODO: Handle nested LOOPs
|
||||
for (int i2 = i + 1; i2 < code.size(); ++i2) {
|
||||
if (code[i2].type == Regular) {
|
||||
// std::cout << "went at "<< i2 << std::endl;
|
||||
instr.flow_control.dest_offset = instr.flow_control.dest_offset + 1;
|
||||
} else if (code[i2].type == EndLoop) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (i2 == code.size() - 1) {
|
||||
throw "No closing EndLoop directive found";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw "Unknown flow control instruction";
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw "Unknown instruction";
|
||||
}
|
||||
|
||||
binary.program.push_back(command.full_instruction.instr);
|
||||
break;
|
||||
}
|
||||
|
||||
case Output:
|
||||
{
|
||||
OutputRegisterInfo output;
|
||||
output.type = command.output_semantic;
|
||||
output.id = command.reg_id;
|
||||
output.component_mask = 0xF; // TODO: Make configurable
|
||||
binary.output_table.push_back(output);
|
||||
break;
|
||||
}
|
||||
|
||||
case Constant:
|
||||
{
|
||||
ConstantInfo constant;
|
||||
constant.type = command.constant_type;
|
||||
constant.regid = command.reg_id;
|
||||
|
||||
switch (command.constant_type) {
|
||||
case ConstantInfo::Float:
|
||||
constant.f.x = to_float24(command.value[0]);
|
||||
constant.f.y = to_float24(command.value[1]);
|
||||
constant.f.z = to_float24(command.value[2]);
|
||||
constant.f.w = to_float24(command.value[3]);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw "Unknown constant type";
|
||||
}
|
||||
binary.constant_table.push_back(constant);
|
||||
break;
|
||||
}
|
||||
|
||||
case Uniform:
|
||||
{
|
||||
UniformInfo uniform;
|
||||
uniform.basic.symbol_offset = binary.symbol_table.size();
|
||||
uniform.basic.reg_start = command.reg_id + 16; // TODO: Hardcoded against float uniforms
|
||||
uniform.basic.reg_end = command.reg_id_last + 16;
|
||||
binary.uniform_table.push_back(uniform);
|
||||
|
||||
std::copy(command.name.begin(), command.name.end(), std::back_inserter(binary.symbol_table));
|
||||
binary.symbol_table.push_back('\0');
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case EndLoop:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw "Unknown type";
|
||||
}
|
||||
}
|
||||
return binary;
|
||||
}
|
||||
|
||||
// Overestimates the actual size
|
||||
static const size_t CompiledShbinSize(std::initializer_list<InlineAsm> code) {
|
||||
size_t size = 0;
|
||||
size += sizeof(DVLBHeader);
|
||||
size += sizeof(DVLPHeader);
|
||||
size += sizeof(uint32_t) + sizeof(DVLEHeader); // Currently only one DVLE is supported
|
||||
|
||||
for (const auto& command : code) {
|
||||
switch (command.type) {
|
||||
case Regular:
|
||||
size += sizeof(Instruction);
|
||||
size += sizeof(SwizzleInfo);
|
||||
break;
|
||||
|
||||
case Output:
|
||||
size += sizeof(OutputRegisterInfo);
|
||||
break;
|
||||
|
||||
case Constant:
|
||||
size += sizeof(ConstantInfo);
|
||||
break;
|
||||
|
||||
case Uniform:
|
||||
size += command.name.size() + 1;
|
||||
size += sizeof(UniformInfo);
|
||||
break;
|
||||
|
||||
case EndLoop:
|
||||
break;
|
||||
|
||||
default:
|
||||
throw "Unknown command type";
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static const std::vector<uint8_t> CompileToShbin(std::initializer_list<InlineAsm> code) {
|
||||
std::vector<uint8_t> ret(CompiledShbinSize(code));
|
||||
|
||||
ShaderBinary bin = CompileToRawBinary(code);
|
||||
|
||||
struct {
|
||||
DVLBHeader header;
|
||||
uint32_t dvle_offset;
|
||||
} *dvlb = (decltype(dvlb))ret.data();
|
||||
dvlb->header.magic_word = DVLBHeader::MAGIC_WORD;
|
||||
dvlb->header.num_programs = 1;
|
||||
|
||||
unsigned dvlp_offset = sizeof(*dvlb);
|
||||
DVLPHeader* dvlp = (DVLPHeader*)&ret.data()[dvlp_offset];
|
||||
dvlp->magic_word = DVLPHeader::MAGIC_WORD;
|
||||
|
||||
unsigned dvle_offset = dvlb->dvle_offset = dvlp_offset + sizeof(DVLPHeader);
|
||||
DVLEHeader* dvle = (DVLEHeader*)&ret.data()[dvle_offset];
|
||||
dvle->magic_word = DVLEHeader::MAGIC_WORD;
|
||||
dvlb->dvle_offset = dvle_offset;
|
||||
|
||||
unsigned binary_offset = dvle_offset + sizeof(DVLEHeader);
|
||||
dvlp->binary_offset = binary_offset - dvlp_offset;
|
||||
dvlp->binary_size_words = bin.program.size();
|
||||
std::copy(bin.program.begin(), bin.program.end(), (Instruction*)&ret.data()[binary_offset]);
|
||||
|
||||
unsigned swizzle_table_offset = binary_offset + bin.program.size() * sizeof(Instruction);
|
||||
dvlp->swizzle_info_offset = swizzle_table_offset - dvlp_offset;
|
||||
dvlp->swizzle_info_num_entries = bin.swizzle_table.size();
|
||||
SwizzleInfo* swizzle_table_ptr = (SwizzleInfo*)&ret.data()[swizzle_table_offset];
|
||||
for (const auto& swizzle : bin.swizzle_table) {
|
||||
swizzle_table_ptr->pattern = swizzle;
|
||||
swizzle_table_ptr->unknown = 0;
|
||||
swizzle_table_ptr++;
|
||||
}
|
||||
|
||||
unsigned output_table_offset = swizzle_table_offset + bin.swizzle_table.size() * sizeof(SwizzleInfo);
|
||||
OutputRegisterInfo* output_table_ptr = (OutputRegisterInfo*)&ret.data()[output_table_offset];
|
||||
for (const auto& output : bin.output_table) {
|
||||
*output_table_ptr = output;
|
||||
output_table_ptr++;
|
||||
}
|
||||
dvle->output_register_table_offset = output_table_offset - dvle_offset;
|
||||
dvle->output_register_table_size = bin.output_table.size();
|
||||
|
||||
unsigned constant_table_offset = output_table_offset + bin.output_table.size() * sizeof(OutputRegisterInfo);
|
||||
ConstantInfo* constant_table_ptr = (ConstantInfo*)&ret.data()[constant_table_offset];
|
||||
for (const auto& constant : bin.constant_table) {
|
||||
*constant_table_ptr = constant;
|
||||
constant_table_ptr++;
|
||||
}
|
||||
dvle->constant_table_offset = constant_table_offset - dvle_offset;
|
||||
dvle->constant_table_size = bin.constant_table.size();
|
||||
|
||||
// TODO: UniformTable spans more than the written data.. fix this design issue :/
|
||||
unsigned uniform_table_offset = constant_table_offset + bin.constant_table.size() * sizeof(ConstantInfo);
|
||||
uint64_t* uniform_table_ptr = (uint64_t*)&ret.data()[uniform_table_offset];
|
||||
for (const auto& uniform : bin.uniform_table) {
|
||||
*uniform_table_ptr = reinterpret_cast<const uint64_t&>(uniform.basic);
|
||||
uniform_table_ptr++;
|
||||
}
|
||||
dvle->uniform_table_offset = uniform_table_offset - dvle_offset;
|
||||
dvle->uniform_table_size = bin.uniform_table.size();
|
||||
|
||||
unsigned symbol_table_offset = uniform_table_offset + bin.uniform_table.size() * sizeof(uint64_t);
|
||||
std::copy(bin.symbol_table.begin(), bin.symbol_table.end(), &ret.data()[symbol_table_offset]);
|
||||
dvle->symbol_table_offset = symbol_table_offset - dvle_offset;
|
||||
dvle->symbol_table_size = bin.symbol_table.size();
|
||||
|
||||
ret.resize(symbol_table_offset + bin.symbol_table.size());
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
384
externals/nihstro/include/nihstro/parser_assembly.h
vendored
Normal file
384
externals/nihstro/include/nihstro/parser_assembly.h
vendored
Normal file
@@ -0,0 +1,384 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <ostream>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#include "source_tree.h"
|
||||
|
||||
#include "shader_binary.h"
|
||||
#include "shader_bytecode.h"
|
||||
|
||||
namespace nihstro {
|
||||
|
||||
struct InputSwizzlerMask {
|
||||
int num_components;
|
||||
|
||||
enum Component : uint8_t {
|
||||
x = 0,
|
||||
y = 1,
|
||||
z = 2,
|
||||
w = 3,
|
||||
};
|
||||
std::array<Component,4> components;
|
||||
|
||||
static InputSwizzlerMask FullMask() {
|
||||
return { 4, {x,y,z,w} };
|
||||
}
|
||||
|
||||
bool operator == (const InputSwizzlerMask& oth) const {
|
||||
return this->num_components == oth.num_components && this->components == oth.components;
|
||||
}
|
||||
|
||||
// TODO: Move to implementation?
|
||||
friend std::ostream& operator<<(std::ostream& os, const Component& v) {
|
||||
switch(v) {
|
||||
case x: return os << "x";
|
||||
case y: return os << "y";
|
||||
case z: return os << "z";
|
||||
case w: return os << "w";
|
||||
default: return os << "?";
|
||||
}
|
||||
}
|
||||
friend std::ostream& operator<<(std::ostream& os, const InputSwizzlerMask& v) {
|
||||
if (!v.num_components)
|
||||
return os << "(empty_mask)";
|
||||
|
||||
for (int i = 0; i < v.num_components; ++i)
|
||||
os << v.components[i];
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
friend std::string to_string(const Component& v) {
|
||||
std::stringstream ss;
|
||||
ss << v;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
friend std::string to_string(const InputSwizzlerMask& v) {
|
||||
std::stringstream ss;
|
||||
ss << v;
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
using Identifier = std::string;
|
||||
|
||||
// A sign, i.e. +1 or -1
|
||||
using Sign = int;
|
||||
|
||||
struct IntegerWithSign {
|
||||
int sign;
|
||||
unsigned value;
|
||||
|
||||
int GetValue() const {
|
||||
return sign * value;
|
||||
}
|
||||
};
|
||||
|
||||
// Raw index + address register index
|
||||
struct IndexExpression : std::vector<boost::variant<IntegerWithSign, Identifier>> {
|
||||
int GetCount() const {
|
||||
return this->size();
|
||||
}
|
||||
|
||||
bool IsRawIndex(int arg) const {
|
||||
return (*this)[arg].which() == 0;
|
||||
}
|
||||
|
||||
int GetRawIndex(int arg) const {
|
||||
return boost::get<IntegerWithSign>((*this)[arg]).GetValue();
|
||||
}
|
||||
|
||||
bool IsAddressRegisterIdentifier(int arg) const {
|
||||
return (*this)[arg].which() == 1;
|
||||
}
|
||||
|
||||
Identifier GetAddressRegisterIdentifier(int arg) const {
|
||||
return boost::get<Identifier>((*this)[arg]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Expression {
|
||||
struct SignedIdentifier {
|
||||
boost::optional<Sign> sign;
|
||||
Identifier identifier;
|
||||
} signed_identifier;
|
||||
|
||||
boost::optional<IndexExpression> index;
|
||||
std::vector<InputSwizzlerMask> swizzle_masks;
|
||||
|
||||
int GetSign() const {
|
||||
if (!RawSign())
|
||||
return +1;
|
||||
else
|
||||
return *RawSign();
|
||||
}
|
||||
|
||||
const Identifier& GetIdentifier() const {
|
||||
return RawIdentifier();
|
||||
}
|
||||
|
||||
bool HasIndexExpression() const {
|
||||
return static_cast<bool>(RawIndex());
|
||||
}
|
||||
|
||||
const IndexExpression& GetIndexExpression() const {
|
||||
return *RawIndex();
|
||||
}
|
||||
|
||||
const std::vector<InputSwizzlerMask>& GetSwizzleMasks() const {
|
||||
return RawSwizzleMasks();
|
||||
}
|
||||
|
||||
private:
|
||||
const boost::optional<Sign>& RawSign() const {
|
||||
return signed_identifier.sign;
|
||||
}
|
||||
|
||||
const Identifier& RawIdentifier() const {
|
||||
return signed_identifier.identifier;
|
||||
}
|
||||
|
||||
const boost::optional<IndexExpression>& RawIndex() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
const std::vector<InputSwizzlerMask>& RawSwizzleMasks() const {
|
||||
return swizzle_masks;
|
||||
}
|
||||
};
|
||||
|
||||
struct ConditionInput {
|
||||
bool invert;
|
||||
Identifier identifier;
|
||||
boost::optional<InputSwizzlerMask> swizzler_mask;
|
||||
|
||||
bool GetInvertFlag() const {
|
||||
return invert;
|
||||
}
|
||||
|
||||
const Identifier& GetIdentifier() const {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
bool HasSwizzleMask() const {
|
||||
return static_cast<bool>(swizzler_mask);
|
||||
}
|
||||
|
||||
const InputSwizzlerMask& GetSwizzleMask() const {
|
||||
return *swizzler_mask;
|
||||
}
|
||||
};
|
||||
|
||||
struct Condition {
|
||||
ConditionInput input1;
|
||||
Instruction::FlowControlType::Op op;
|
||||
ConditionInput input2;
|
||||
|
||||
const ConditionInput& GetFirstInput() const {
|
||||
return input1;
|
||||
}
|
||||
|
||||
Instruction::FlowControlType::Op GetConditionOp() const {
|
||||
return op;
|
||||
}
|
||||
|
||||
const ConditionInput& GetSecondInput() const {
|
||||
return input2;
|
||||
}
|
||||
};
|
||||
|
||||
using StatementLabel = std::string;
|
||||
|
||||
struct StatementInstruction {
|
||||
OpCode opcode;
|
||||
std::vector<Expression> expressions;
|
||||
|
||||
StatementInstruction() = default;
|
||||
|
||||
// TODO: Obsolete constructor?
|
||||
StatementInstruction(const OpCode& opcode) : opcode(opcode) {
|
||||
}
|
||||
|
||||
StatementInstruction(const OpCode& opcode, const std::vector<Expression> expressions) : opcode(opcode), expressions(expressions) {
|
||||
}
|
||||
|
||||
const OpCode& GetOpCode() const {
|
||||
return opcode;
|
||||
}
|
||||
|
||||
const std::vector<Expression>& GetArguments() const {
|
||||
return expressions;
|
||||
}
|
||||
};
|
||||
using FloatOpInstruction = StatementInstruction;
|
||||
|
||||
struct CompareInstruction {
|
||||
OpCode opcode;
|
||||
std::vector<Expression> arguments;
|
||||
std::vector<Instruction::Common::CompareOpType::Op> ops;
|
||||
|
||||
const OpCode& GetOpCode() const {
|
||||
return opcode;
|
||||
}
|
||||
|
||||
const Expression& GetSrc1() const {
|
||||
return arguments[0];
|
||||
}
|
||||
|
||||
const Expression& GetSrc2() const {
|
||||
return arguments[1];
|
||||
}
|
||||
|
||||
Instruction::Common::CompareOpType::Op GetOp1() const {
|
||||
return ops[0];
|
||||
}
|
||||
|
||||
Instruction::Common::CompareOpType::Op GetOp2() const {
|
||||
return ops[1];
|
||||
}
|
||||
};
|
||||
|
||||
struct FlowControlInstruction {
|
||||
OpCode opcode;
|
||||
std::string target_label;
|
||||
boost::optional<std::string> return_label;
|
||||
boost::optional<Condition> condition;
|
||||
|
||||
const OpCode& GetOpCode() const {
|
||||
return opcode;
|
||||
}
|
||||
|
||||
const std::string& GetTargetLabel() const {
|
||||
return target_label;
|
||||
}
|
||||
|
||||
bool HasReturnLabel() const {
|
||||
return static_cast<bool>(return_label);
|
||||
}
|
||||
|
||||
const std::string& GetReturnLabel() const {
|
||||
return *return_label;
|
||||
}
|
||||
|
||||
bool HasCondition() const {
|
||||
return static_cast<bool>(condition);
|
||||
}
|
||||
|
||||
const Condition& GetCondition() const {
|
||||
return *condition;
|
||||
}
|
||||
};
|
||||
|
||||
struct SetEmitInstruction {
|
||||
OpCode opcode;
|
||||
unsigned vertex_id;
|
||||
|
||||
struct Flags {
|
||||
boost::optional<bool> primitive_flag;
|
||||
boost::optional<bool> invert_flag;
|
||||
} flags;
|
||||
|
||||
bool PrimitiveFlag() const {
|
||||
return flags.primitive_flag && *flags.primitive_flag;
|
||||
}
|
||||
|
||||
bool InvertFlag() const {
|
||||
return flags.invert_flag && *flags.invert_flag;
|
||||
}
|
||||
};
|
||||
|
||||
struct StatementDeclaration {
|
||||
std::string alias_name;
|
||||
Identifier identifier_start; /* aliased identifier (start register) */
|
||||
boost::optional<Identifier> identifier_end; /* aliased identifier (end register) */
|
||||
boost::optional<InputSwizzlerMask> swizzle_mask; // referring to the aliased identifier
|
||||
|
||||
struct Extra {
|
||||
std::vector<float> constant_value;
|
||||
boost::optional<OutputRegisterInfo::Type> output_semantic;
|
||||
} extra;
|
||||
};
|
||||
|
||||
struct ParserContext {
|
||||
// There currently is no context
|
||||
};
|
||||
|
||||
|
||||
struct Parser {
|
||||
using Iterator = SourceTreeIterator;
|
||||
|
||||
Parser(const ParserContext& context);
|
||||
~Parser();
|
||||
|
||||
// Skip whitespaces, blank lines, and comments; returns number of line breaks skipped.
|
||||
unsigned Skip(Iterator& begin, Iterator end);
|
||||
|
||||
// Skip to the next line
|
||||
void SkipSingleLine(Iterator& begin, Iterator end);
|
||||
|
||||
// Parse alias declaration including line ending
|
||||
bool ParseDeclaration(Iterator& begin, Iterator end, StatementDeclaration* declaration);
|
||||
|
||||
// Parse label declaration including line ending
|
||||
bool ParseLabel(Iterator& begin, Iterator end, StatementLabel* label);
|
||||
|
||||
// Parse nothing but a single opcode
|
||||
bool ParseOpCode(Iterator& begin, Iterator end, OpCode* opcode);
|
||||
|
||||
// Parse trival instruction including line ending
|
||||
bool ParseSimpleInstruction(Iterator& begin, Iterator end, OpCode* opcode);
|
||||
|
||||
// Parse float instruction including line ending
|
||||
bool ParseFloatOp(Iterator& begin, Iterator end, FloatOpInstruction* content);
|
||||
|
||||
// Parse compare instruction including line ending
|
||||
bool ParseCompare(Iterator& begin, Iterator end, CompareInstruction* content);
|
||||
|
||||
// Parse flow control instruction including line ending
|
||||
bool ParseFlowControl(Iterator& begin, Iterator end, FlowControlInstruction* content);
|
||||
|
||||
// Parse SetEmit instruction including line ending
|
||||
bool ParseSetEmit(Iterator& begin, Iterator end, SetEmitInstruction* content);
|
||||
|
||||
private:
|
||||
struct ParserImpl;
|
||||
std::unique_ptr<ParserImpl> impl;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
376
externals/nihstro/include/nihstro/parser_assembly_private.h
vendored
Normal file
376
externals/nihstro/include/nihstro/parser_assembly_private.h
vendored
Normal file
@@ -0,0 +1,376 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
// Enable this for detailed XML overview of parser results
|
||||
// #define BOOST_SPIRIT_DEBUG
|
||||
|
||||
#include <boost/fusion/include/adapt_struct.hpp>
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
|
||||
#include "nihstro/parser_assembly.h"
|
||||
#include "nihstro/source_tree.h"
|
||||
|
||||
#include "nihstro/shader_binary.h"
|
||||
#include "nihstro/shader_bytecode.h"
|
||||
|
||||
namespace spirit = boost::spirit;
|
||||
namespace qi = boost::spirit::qi;
|
||||
namespace ascii = boost::spirit::qi::ascii;
|
||||
namespace phoenix = boost::phoenix;
|
||||
|
||||
using spirit::_1;
|
||||
using spirit::_2;
|
||||
using spirit::_3;
|
||||
using spirit::_4;
|
||||
|
||||
using namespace nihstro;
|
||||
|
||||
// Adapt common parser data structures for use with boost::spirit
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
Expression::SignedIdentifier,
|
||||
(boost::optional<Sign>, sign)
|
||||
(Identifier, identifier)
|
||||
)
|
||||
|
||||
BOOST_FUSION_ADAPT_STRUCT(
|
||||
Expression,
|
||||
(Expression::SignedIdentifier, signed_identifier)
|
||||
(boost::optional<IndexExpression>, index)
|
||||
(std::vector<InputSwizzlerMask>, swizzle_masks)
|
||||
)
|
||||
|
||||
class Diagnostics
|
||||
{
|
||||
public:
|
||||
// Ass a new diagnostic message corresponding to the specified rule tag
|
||||
void Add(const std::string& tag, const char* diagnostic) {
|
||||
entries[tag] = diagnostic;
|
||||
}
|
||||
|
||||
// Lookup the diagnostic of the specified rule tag and return it (or nullptr if it can't be found)
|
||||
const char* operator [](const char* tag) const {
|
||||
auto it = entries.find(tag);
|
||||
if (it == entries.end())
|
||||
return nullptr;
|
||||
else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<std::string, const char*> entries;
|
||||
};
|
||||
|
||||
struct ErrorHandler
|
||||
{
|
||||
template <class, class, class, class, class>
|
||||
struct result { typedef void type; };
|
||||
|
||||
template <class D, class B, class E, class W, class I>
|
||||
void operator ()(const D& diagnostics, B begin, E end, W where, const I& info) const
|
||||
{
|
||||
const spirit::utf8_string& tag(info.tag);
|
||||
const char* const what(tag.c_str());
|
||||
const char* diagnostic(diagnostics[what]);
|
||||
std::string scratch;
|
||||
if (!diagnostic) {
|
||||
scratch.reserve(25 + tag.length());
|
||||
scratch = "Expected ";
|
||||
scratch += tag;
|
||||
diagnostic = scratch.c_str();
|
||||
}
|
||||
|
||||
auto newline_iterator = std::find(begin, end, '\n');
|
||||
|
||||
std::stringstream err;
|
||||
err << diagnostic << std::endl
|
||||
<< std::string(4, ' ') << std::string(begin, newline_iterator) << std::endl
|
||||
<< std::string(4 + std::distance(begin, where), ' ') << '^' << std::endl;
|
||||
throw err.str();
|
||||
}
|
||||
};
|
||||
extern phoenix::function<ErrorHandler> error_handler;
|
||||
|
||||
template<typename Iterator>
|
||||
struct AssemblySkipper : public qi::grammar<Iterator> {
|
||||
|
||||
AssemblySkipper() : AssemblySkipper::base_type(skip) {
|
||||
comments = (qi::lit("//") | '#' | ';') >> *(qi::char_ - qi::eol);
|
||||
|
||||
skip = +(comments | ascii::blank);
|
||||
}
|
||||
|
||||
qi::rule<Iterator> comments;
|
||||
qi::rule<Iterator> skip;
|
||||
};
|
||||
|
||||
namespace std {
|
||||
|
||||
static std::ostream& operator<<(std::ostream& os, const OpCode& opcode) {
|
||||
// TODO: Should print actual opcode here..
|
||||
return os << static_cast<uint32_t>(static_cast<OpCode::Id>(opcode));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename Iterator>
|
||||
struct CommonRules {
|
||||
using Skipper = AssemblySkipper<Iterator>;
|
||||
|
||||
CommonRules(const ParserContext& context);
|
||||
|
||||
// Rule-ified symbols, which can be assigned names
|
||||
qi::rule<Iterator, Skipper> peek_identifier;
|
||||
|
||||
// Building blocks
|
||||
qi::rule<Iterator, std::string(), Skipper> identifier;
|
||||
qi::rule<Iterator, Expression(), Skipper> expression;
|
||||
qi::rule<Iterator, Skipper> end_of_statement;
|
||||
|
||||
qi::symbols<char, OpCode> opcodes_trivial;
|
||||
qi::symbols<char, OpCode> opcodes_compare;
|
||||
std::array<qi::symbols<char, OpCode>, 4> opcodes_float; // indexed by number of arguments
|
||||
std::array<qi::symbols<char, OpCode>, 2> opcodes_flowcontrol;
|
||||
qi::symbols<char, OpCode> opcodes_setemit;
|
||||
|
||||
qi::symbols<char, int> signs;
|
||||
|
||||
qi::symbols<char, InputSwizzlerMask::Component> swizzlers;
|
||||
qi::rule<Iterator, InputSwizzlerMask(), Skipper> swizzle_mask;
|
||||
|
||||
Diagnostics diagnostics;
|
||||
|
||||
private:
|
||||
qi::rule<Iterator, IndexExpression(), Skipper> index_expression;
|
||||
qi::rule<Iterator, boost::variant<IntegerWithSign, Identifier>(), Skipper> index_expression_first_term;
|
||||
qi::rule<Iterator, boost::variant<IntegerWithSign, Identifier>(), Skipper> index_expression_following_terms;
|
||||
|
||||
// Empty rule
|
||||
qi::rule<Iterator, Skipper> opening_bracket;
|
||||
qi::rule<Iterator, Skipper> closing_bracket;
|
||||
qi::rule<Iterator, IntegerWithSign(), Skipper> sign_with_uint;
|
||||
qi::rule<Iterator, unsigned int(), Skipper> uint_after_sign;
|
||||
};
|
||||
|
||||
template<typename Iterator, bool require_end_of_line>
|
||||
struct TrivialOpParser : qi::grammar<Iterator, OpCode(), AssemblySkipper<Iterator>> {
|
||||
using Skipper = AssemblySkipper<Iterator>;
|
||||
|
||||
TrivialOpParser(const ParserContext& context);
|
||||
|
||||
CommonRules<Iterator> common;
|
||||
|
||||
qi::symbols<char, OpCode>& opcodes_trivial;
|
||||
qi::symbols<char, OpCode>& opcodes_compare;
|
||||
std::array<qi::symbols<char, OpCode>, 4>& opcodes_float; // indexed by number of arguments
|
||||
std::array<qi::symbols<char, OpCode>, 2>& opcodes_flowcontrol;
|
||||
|
||||
// Rule-ified symbols, which can be assigned names
|
||||
qi::rule<Iterator, OpCode(), Skipper> opcode;
|
||||
|
||||
// Compounds
|
||||
qi::rule<Iterator, OpCode(), Skipper> trivial_instruction;
|
||||
qi::rule<Iterator, Skipper>& end_of_statement;
|
||||
|
||||
Diagnostics diagnostics;
|
||||
};
|
||||
|
||||
template<typename Iterator>
|
||||
struct FloatOpParser : qi::grammar<Iterator, FloatOpInstruction(), AssemblySkipper<Iterator>> {
|
||||
using Skipper = AssemblySkipper<Iterator>;
|
||||
|
||||
FloatOpParser(const ParserContext& context);
|
||||
|
||||
CommonRules<Iterator> common;
|
||||
|
||||
std::array<qi::symbols<char, OpCode>, 4>& opcodes_float;
|
||||
|
||||
// Rule-ified symbols, which can be assigned names
|
||||
qi::rule<Iterator, OpCode(), Skipper> opcode[4];
|
||||
|
||||
// Building blocks
|
||||
qi::rule<Iterator, Expression(), Skipper>& expression;
|
||||
qi::rule<Iterator, std::vector<Expression>(), Skipper> expression_chain[4]; // sequence of instruction arguments
|
||||
qi::rule<Iterator, Skipper>& end_of_statement;
|
||||
|
||||
// Compounds
|
||||
qi::rule<Iterator, FloatOpInstruction(), Skipper> float_instr[4];
|
||||
qi::rule<Iterator, FloatOpInstruction(), Skipper> float_instruction;
|
||||
|
||||
// Utility
|
||||
qi::rule<Iterator, Skipper> not_comma;
|
||||
|
||||
Diagnostics diagnostics;
|
||||
};
|
||||
|
||||
template<typename Iterator>
|
||||
struct CompareParser : qi::grammar<Iterator, CompareInstruction(), AssemblySkipper<Iterator>> {
|
||||
using Skipper = AssemblySkipper<Iterator>;
|
||||
using CompareOp = Instruction::Common::CompareOpType;
|
||||
using CompareOpEnum = CompareOp::Op;
|
||||
|
||||
CompareParser(const ParserContext& context);
|
||||
|
||||
CommonRules<Iterator> common;
|
||||
|
||||
qi::symbols<char, OpCode>& opcodes_compare;
|
||||
qi::symbols<char, CompareOpEnum> compare_ops;
|
||||
|
||||
// Rule-ified symbols, which can be assigned debug names
|
||||
qi::rule<Iterator, OpCode(), Skipper> opcode;
|
||||
qi::rule<Iterator, CompareOpEnum(), Skipper> compare_op;
|
||||
qi::rule<Iterator, std::vector<CompareOpEnum>(), Skipper> two_ops;
|
||||
|
||||
// Building blocks
|
||||
qi::rule<Iterator, Expression(), Skipper>& expression;
|
||||
qi::rule<Iterator, std::vector<Expression>(), Skipper> two_expressions;
|
||||
qi::rule<Iterator, Skipper>& end_of_statement;
|
||||
|
||||
// Compounds
|
||||
qi::rule<Iterator, CompareInstruction(), Skipper> instr[1];
|
||||
qi::rule<Iterator, CompareInstruction(), Skipper> instruction;
|
||||
|
||||
// Utility
|
||||
qi::rule<Iterator, Skipper> not_comma;
|
||||
|
||||
Diagnostics diagnostics;
|
||||
};
|
||||
|
||||
template<typename Iterator>
|
||||
struct FlowControlParser : qi::grammar<Iterator, FlowControlInstruction(), AssemblySkipper<Iterator>> {
|
||||
using Skipper = AssemblySkipper<Iterator>;
|
||||
using ConditionOp = Instruction::FlowControlType;
|
||||
using ConditionOpEnum = Instruction::FlowControlType::Op;
|
||||
|
||||
FlowControlParser(const ParserContext& context);
|
||||
|
||||
CommonRules<Iterator> common;
|
||||
|
||||
std::array<qi::symbols<char, OpCode>, 2>& opcodes_flowcontrol;
|
||||
qi::symbols<char, ConditionOpEnum> condition_ops;
|
||||
|
||||
// Rule-ified symbols, which can be assigned debug names
|
||||
qi::rule<Iterator, OpCode(), Skipper> opcode[2];
|
||||
qi::rule<Iterator, ConditionOpEnum(), Skipper> condition_op;
|
||||
|
||||
// Building blocks
|
||||
qi::rule<Iterator, Expression(), Skipper>& expression;
|
||||
qi::rule<Iterator, std::string(), Skipper>& identifier;
|
||||
qi::rule<Iterator, InputSwizzlerMask(), Skipper>& swizzle_mask;
|
||||
qi::rule<Iterator, ConditionInput(), Skipper> condition_input;
|
||||
qi::rule<Iterator, Condition(), Skipper> condition;
|
||||
qi::rule<Iterator, Skipper>& end_of_statement;
|
||||
|
||||
// Compounds
|
||||
qi::rule<Iterator, FlowControlInstruction(), Skipper> instr[2];
|
||||
qi::rule<Iterator, FlowControlInstruction(), Skipper> flow_control_instruction;
|
||||
|
||||
// Utility
|
||||
qi::rule<Iterator, Skipper> not_comma;
|
||||
qi::rule<Iterator, bool(), Skipper> negation;
|
||||
|
||||
Diagnostics diagnostics;
|
||||
};
|
||||
|
||||
template<typename Iterator>
|
||||
struct SetEmitParser : qi::grammar<Iterator, SetEmitInstruction(), AssemblySkipper<Iterator>> {
|
||||
using Skipper = AssemblySkipper<Iterator>;
|
||||
|
||||
SetEmitParser(const ParserContext& context);
|
||||
|
||||
CommonRules<Iterator> common;
|
||||
|
||||
qi::symbols<char, OpCode>& opcodes_setemit;
|
||||
|
||||
// Rule-ified symbols, which can be assigned debug names
|
||||
qi::rule<Iterator, OpCode(), Skipper> opcode;
|
||||
qi::rule<Iterator, unsigned int(), Skipper> vertex_id;
|
||||
qi::rule<Iterator, bool(), Skipper> prim_flag;
|
||||
qi::rule<Iterator, bool(), Skipper> inv_flag;
|
||||
qi::rule<Iterator, SetEmitInstruction::Flags(), Skipper> flags;
|
||||
|
||||
// Building blocks
|
||||
qi::rule<Iterator, Skipper>& end_of_statement;
|
||||
|
||||
// Compounds
|
||||
qi::rule<Iterator, SetEmitInstruction(), Skipper> setemit_instruction;
|
||||
|
||||
// Utility
|
||||
qi::rule<Iterator, Skipper> not_comma;
|
||||
qi::rule<Iterator, bool(), Skipper> negation;
|
||||
|
||||
Diagnostics diagnostics;
|
||||
};
|
||||
|
||||
template<typename Iterator>
|
||||
struct LabelParser : qi::grammar<Iterator, StatementLabel(), AssemblySkipper<Iterator>> {
|
||||
using Skipper = AssemblySkipper<Iterator>;
|
||||
|
||||
LabelParser(const ParserContext& context);
|
||||
|
||||
CommonRules<Iterator> common;
|
||||
|
||||
qi::rule<Iterator, Skipper>& end_of_statement;
|
||||
|
||||
qi::rule<Iterator, std::string(), Skipper>& identifier;
|
||||
qi::rule<Iterator, std::string(), Skipper> label;
|
||||
|
||||
Diagnostics diagnostics;
|
||||
};
|
||||
|
||||
template<typename Iterator>
|
||||
struct DeclarationParser : qi::grammar<Iterator, StatementDeclaration(), AssemblySkipper<Iterator>> {
|
||||
using Skipper = AssemblySkipper<Iterator>;
|
||||
|
||||
DeclarationParser(const ParserContext& context);
|
||||
|
||||
CommonRules<Iterator> common;
|
||||
|
||||
qi::rule<Iterator, Skipper> string_as;
|
||||
qi::rule<Iterator, std::vector<float>(), Skipper> dummy_const;
|
||||
qi::rule<Iterator, boost::optional<OutputRegisterInfo::Type>(), Skipper> dummy_semantic;
|
||||
|
||||
qi::symbols<char, OutputRegisterInfo::Type> output_semantics;
|
||||
|
||||
// Rule-ified symbols, which can be assigned names
|
||||
qi::rule<Iterator, OutputRegisterInfo::Type(),Skipper> output_semantics_rule;
|
||||
|
||||
// Building blocks
|
||||
qi::rule<Iterator, std::string(), Skipper>& identifier;
|
||||
qi::rule<Iterator, InputSwizzlerMask(), Skipper>& swizzle_mask;
|
||||
qi::rule<Iterator, std::vector<float>(), Skipper> constant;
|
||||
qi::rule<Iterator, std::string(), Skipper> alias_identifier;
|
||||
qi::rule<Iterator, StatementDeclaration::Extra(), Skipper> const_or_semantic;
|
||||
qi::rule<Iterator, Skipper>& end_of_statement;
|
||||
|
||||
qi::rule<Iterator, StatementDeclaration(), Skipper> declaration;
|
||||
Diagnostics diagnostics;
|
||||
};
|
||||
|
||||
using ParserIterator = SourceTreeIterator;
|
||||
207
externals/nihstro/include/nihstro/parser_shbin.h
vendored
Normal file
207
externals/nihstro/include/nihstro/parser_shbin.h
vendored
Normal file
@@ -0,0 +1,207 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "nihstro/shader_binary.h"
|
||||
|
||||
namespace nihstro {
|
||||
|
||||
struct ShaderInfo {
|
||||
std::vector<Instruction> code;
|
||||
std::vector<SwizzleInfo> swizzle_info;
|
||||
|
||||
std::vector<ConstantInfo> constant_table;
|
||||
std::vector<LabelInfo> label_table;
|
||||
std::map<uint32_t, std::string> labels;
|
||||
std::vector<OutputRegisterInfo> output_register_info;
|
||||
std::vector<UniformInfo> uniform_table;
|
||||
|
||||
void Clear() {
|
||||
code.clear();
|
||||
swizzle_info.clear();
|
||||
constant_table.clear();
|
||||
label_table.clear();
|
||||
labels.clear();
|
||||
output_register_info.clear();
|
||||
uniform_table.clear();
|
||||
}
|
||||
|
||||
bool HasLabel(uint32_t offset) const {
|
||||
return labels.find(offset) != labels.end();
|
||||
}
|
||||
|
||||
std::string GetLabel (uint32_t offset) const {
|
||||
auto it = labels.find(offset);
|
||||
if (it != labels.end())
|
||||
return it->second;
|
||||
return "";
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string LookupDestName(const T& dest, const SwizzlePattern& swizzle) const {
|
||||
if (dest < 0x8) {
|
||||
// TODO: This one still needs some prettification in case
|
||||
// multiple output_infos describing this output register
|
||||
// are found.
|
||||
std::string ret;
|
||||
for (const auto& output_info : output_register_info) {
|
||||
if (dest != output_info.id)
|
||||
continue;
|
||||
|
||||
// Only display output register name if the output components it's mapped to are
|
||||
// actually written to.
|
||||
// swizzle.dest_mask and output_info.component_mask use different bit order,
|
||||
// so we can't use AND them bitwise to check this.
|
||||
int matching_mask = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
matching_mask |= output_info.component_mask & (swizzle.DestComponentEnabled(i) << i);
|
||||
|
||||
if (!matching_mask)
|
||||
continue;
|
||||
|
||||
// Add a vertical bar so that we have at least *some*
|
||||
// indication that we hit multiple matches.
|
||||
if (!ret.empty())
|
||||
ret += "|";
|
||||
|
||||
ret += output_info.GetSemanticName();
|
||||
}
|
||||
if (!ret.empty())
|
||||
return ret;
|
||||
} else if (dest.GetRegisterType() == RegisterType::Temporary) {
|
||||
// TODO: Not sure if uniform_info can assign names to temporary registers.
|
||||
// If that is the case, we should check the table for better names here.
|
||||
std::stringstream stream;
|
||||
stream << "temp_" << std::hex << dest.GetIndex();
|
||||
return stream.str();
|
||||
}
|
||||
return "(?)";
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::string LookupSourceName(const T& source, unsigned addr_reg_index) const {
|
||||
if (source.GetRegisterType() != RegisterType::Temporary) {
|
||||
for (const auto& uniform_info : uniform_table) {
|
||||
// Magic numbers are needed because uniform info registers use the
|
||||
// range 0..0x10 for input registers and 0x10...0x70 for uniform registers,
|
||||
// i.e. there is a "gap" at the temporary registers, for which no
|
||||
// name can be assigned (?).
|
||||
int off = (source.GetRegisterType() == RegisterType::Input) ? 0 : 0x10;
|
||||
if (source - off >= uniform_info.basic.reg_start &&
|
||||
source - off <= uniform_info.basic.reg_end) {
|
||||
std::string name = uniform_info.name;
|
||||
|
||||
std::string index;
|
||||
bool is_array = uniform_info.basic.reg_end != uniform_info.basic.reg_start;
|
||||
if (is_array) {
|
||||
index += std::to_string(source - off - uniform_info.basic.reg_start);
|
||||
}
|
||||
if (addr_reg_index != 0) {
|
||||
index += (is_array) ? " + " : "";
|
||||
index += "a" + std::to_string(addr_reg_index - 1);
|
||||
}
|
||||
|
||||
if (!index.empty())
|
||||
name += "[" + index + "]";
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Constants and uniforms really are the same internally
|
||||
for (const auto& constant_info : constant_table) {
|
||||
if (source - 0x20 == constant_info.regid) {
|
||||
return "const_" + std::to_string(constant_info.regid.Value());
|
||||
}
|
||||
}
|
||||
|
||||
// For temporary registers, we at least print "temp_X" if no better name could be found.
|
||||
if (source.GetRegisterType() == RegisterType::Temporary) {
|
||||
std::stringstream stream;
|
||||
stream << "temp_" << std::hex << source.GetIndex();
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
return "(?)";
|
||||
}
|
||||
};
|
||||
|
||||
class ShbinParser {
|
||||
public:
|
||||
void ReadHeaders(const std::string& filename);
|
||||
|
||||
void ReadDVLE(int dvle_index);
|
||||
|
||||
const DVLBHeader& GetDVLBHeader() const {
|
||||
return dvlb_header;
|
||||
}
|
||||
|
||||
const DVLPHeader& GetDVLPHeader() const {
|
||||
return dvlp_header;
|
||||
}
|
||||
|
||||
const DVLEHeader& GetDVLEHeader(int index) const {
|
||||
return dvle_headers[index];
|
||||
}
|
||||
|
||||
const std::string& GetFilename(int dvle_index) const {
|
||||
return dvle_filenames[dvle_index];
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// Reads a null-terminated string from the given offset
|
||||
std::string ReadSymbol(uint32_t offset);
|
||||
|
||||
std::fstream file;
|
||||
|
||||
|
||||
DVLBHeader dvlb_header;
|
||||
DVLPHeader dvlp_header;
|
||||
|
||||
uint32_t dvlp_offset;
|
||||
|
||||
public:
|
||||
std::vector<uint32_t> dvle_offsets;
|
||||
std::vector<DVLEHeader> dvle_headers;
|
||||
std::vector<std::string> dvle_filenames;
|
||||
|
||||
ShaderInfo shader_info;
|
||||
|
||||
uint32_t main_offset;
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
36
externals/nihstro/include/nihstro/preprocessor.h
vendored
Normal file
36
externals/nihstro/include/nihstro/preprocessor.h
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2015 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace nihstro {
|
||||
|
||||
struct SourceTree;
|
||||
|
||||
SourceTree PreprocessAssemblyFile(const std::string& filename);
|
||||
|
||||
} // namespace
|
||||
258
externals/nihstro/include/nihstro/shader_binary.h
vendored
Normal file
258
externals/nihstro/include/nihstro/shader_binary.h
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "shader_bytecode.h"
|
||||
|
||||
namespace nihstro {
|
||||
|
||||
#pragma pack(1)
|
||||
struct DVLBHeader {
|
||||
enum : uint32_t {
|
||||
MAGIC_WORD = 0x424C5644, // "DVLB"
|
||||
};
|
||||
|
||||
uint32_t magic_word;
|
||||
uint32_t num_programs;
|
||||
|
||||
// DVLE offset table with num_programs entries follows
|
||||
};
|
||||
static_assert(sizeof(DVLBHeader) == 0x8, "Incorrect structure size");
|
||||
|
||||
struct DVLPHeader {
|
||||
enum : uint32_t {
|
||||
MAGIC_WORD = 0x504C5644, // "DVLP"
|
||||
};
|
||||
|
||||
uint32_t magic_word;
|
||||
uint32_t version;
|
||||
uint32_t binary_offset; // relative to DVLP start
|
||||
uint32_t binary_size_words;
|
||||
uint32_t swizzle_info_offset;
|
||||
uint32_t swizzle_info_num_entries;
|
||||
uint32_t filename_symbol_offset;
|
||||
};
|
||||
static_assert(sizeof(DVLPHeader) == 0x1C, "Incorrect structure size");
|
||||
|
||||
struct DVLEHeader {
|
||||
enum : uint32_t {
|
||||
MAGIC_WORD = 0x454c5644, // "DVLE"
|
||||
};
|
||||
|
||||
enum class ShaderType : uint8_t {
|
||||
VERTEX = 0,
|
||||
GEOMETRY = 1,
|
||||
};
|
||||
|
||||
uint32_t magic_word;
|
||||
uint16_t pad1;
|
||||
ShaderType type;
|
||||
uint8_t pad2;
|
||||
|
||||
// Offset within binary blob to program entry point
|
||||
uint32_t main_offset_words;
|
||||
uint32_t endmain_offset_words;
|
||||
|
||||
uint32_t pad3;
|
||||
uint32_t pad4;
|
||||
|
||||
// Table of constant values for single registers
|
||||
uint32_t constant_table_offset;
|
||||
uint32_t constant_table_size; // number of entries
|
||||
|
||||
// Table of program code labels
|
||||
uint32_t label_table_offset;
|
||||
uint32_t label_table_size;
|
||||
|
||||
// Table of output registers and their semantics
|
||||
uint32_t output_register_table_offset;
|
||||
uint32_t output_register_table_size;
|
||||
|
||||
// Table of uniforms (which may span multiple registers) and their values
|
||||
uint32_t uniform_table_offset;
|
||||
uint32_t uniform_table_size;
|
||||
|
||||
// Table of null-terminated strings referenced by the tables above
|
||||
uint32_t symbol_table_offset;
|
||||
uint32_t symbol_table_size;
|
||||
|
||||
};
|
||||
static_assert(sizeof(DVLEHeader) == 0x40, "Incorrect structure size");
|
||||
|
||||
|
||||
struct SwizzleInfo {
|
||||
SwizzlePattern pattern;
|
||||
uint32_t unknown;
|
||||
};
|
||||
|
||||
struct ConstantInfo {
|
||||
enum Type : uint32_t {
|
||||
Bool = 0,
|
||||
Int = 1,
|
||||
Float = 2
|
||||
};
|
||||
|
||||
union {
|
||||
uint32_t full_first_word;
|
||||
|
||||
BitField<0, 2, Type> type;
|
||||
|
||||
BitField<16, 8, uint32_t> regid;
|
||||
};
|
||||
|
||||
union {
|
||||
uint32_t value_hex[4];
|
||||
|
||||
BitField<0, 1, uint32_t> b;
|
||||
|
||||
struct {
|
||||
uint8_t x;
|
||||
uint8_t y;
|
||||
uint8_t z;
|
||||
uint8_t w;
|
||||
} i;
|
||||
|
||||
struct {
|
||||
// All of these are float24 values!
|
||||
uint32_t x;
|
||||
uint32_t y;
|
||||
uint32_t z;
|
||||
uint32_t w;
|
||||
} f;
|
||||
};
|
||||
};
|
||||
|
||||
struct LabelInfo {
|
||||
BitField<0, 8, uint32_t> id;
|
||||
uint32_t program_offset;
|
||||
uint32_t unk;
|
||||
uint32_t name_offset;
|
||||
};
|
||||
|
||||
union OutputRegisterInfo {
|
||||
enum Type : uint64_t {
|
||||
POSITION = 0,
|
||||
QUATERNION = 1,
|
||||
COLOR = 2,
|
||||
TEXCOORD0 = 3,
|
||||
|
||||
TEXCOORD1 = 5,
|
||||
TEXCOORD2 = 6,
|
||||
|
||||
VIEW = 8,
|
||||
};
|
||||
|
||||
OutputRegisterInfo& operator =(const OutputRegisterInfo& oth) {
|
||||
hex.Assign(oth.hex);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BitField< 0, 64, uint64_t> hex;
|
||||
|
||||
BitField< 0, 16, Type> type;
|
||||
BitField<16, 16, uint64_t> id;
|
||||
BitField<32, 4, uint64_t> component_mask;
|
||||
BitField<32, 32, uint64_t> descriptor;
|
||||
|
||||
const std::string GetMask() const {
|
||||
std::string ret;
|
||||
if (component_mask & 1) ret += "x";
|
||||
if (component_mask & 2) ret += "y";
|
||||
if (component_mask & 4) ret += "z";
|
||||
if (component_mask & 8) ret += "w";
|
||||
return ret;
|
||||
}
|
||||
|
||||
const std::string GetSemanticName() const {
|
||||
static const std::map<Type, std::string> map = {
|
||||
{ POSITION, "out.pos" },
|
||||
{ QUATERNION, "out.quat" },
|
||||
{ COLOR, "out.col" },
|
||||
{ TEXCOORD0, "out.tex0" },
|
||||
{ TEXCOORD1, "out.tex1" },
|
||||
{ TEXCOORD2, "out.tex2" },
|
||||
{ VIEW, "out.view" }
|
||||
};
|
||||
auto it = map.find(type);
|
||||
if (it != map.end())
|
||||
return it->second;
|
||||
else
|
||||
return "out.unk";
|
||||
}
|
||||
};
|
||||
|
||||
struct UniformInfo {
|
||||
struct {
|
||||
static RegisterType GetType(uint32_t reg) {
|
||||
if (reg < 0x10) return RegisterType::Input;
|
||||
else if (reg < 0x70) return RegisterType::FloatUniform;
|
||||
else if (reg < 0x74) return RegisterType::IntUniform;
|
||||
else if (reg >= 0x78 && reg < 0x88) return RegisterType::BoolUniform;
|
||||
else return RegisterType::Unknown;
|
||||
}
|
||||
|
||||
static int GetIndex(uint32_t reg) {
|
||||
switch (GetType(reg)) {
|
||||
case RegisterType::Input: return reg;
|
||||
case RegisterType::FloatUniform: return reg - 0x10;
|
||||
case RegisterType::IntUniform: return reg - 0x70;
|
||||
case RegisterType::BoolUniform: return reg - 0x78;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
RegisterType GetStartType() const {
|
||||
return GetType(reg_start);
|
||||
}
|
||||
|
||||
RegisterType GetEndType() const {
|
||||
return GetType(reg_end);
|
||||
}
|
||||
|
||||
int GetStartIndex() const {
|
||||
return GetIndex(reg_start);
|
||||
}
|
||||
|
||||
int GetEndIndex() const {
|
||||
return GetIndex(reg_end);
|
||||
}
|
||||
|
||||
uint32_t symbol_offset;
|
||||
union {
|
||||
BitField< 0, 16, uint32_t> reg_start;
|
||||
BitField<16, 16, uint32_t> reg_end; // inclusive
|
||||
};
|
||||
} basic;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
} // namespace
|
||||
813
externals/nihstro/include/nihstro/shader_bytecode.h
vendored
Normal file
813
externals/nihstro/include/nihstro/shader_bytecode.h
vendored
Normal file
@@ -0,0 +1,813 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include "bit_field.h"
|
||||
|
||||
namespace nihstro {
|
||||
|
||||
enum class RegisterType {
|
||||
Input,
|
||||
Output,
|
||||
Temporary,
|
||||
FloatUniform,
|
||||
IntUniform,
|
||||
BoolUniform,
|
||||
Address,
|
||||
ConditionalCode,
|
||||
Unknown
|
||||
};
|
||||
|
||||
static std::string GetRegisterName(RegisterType type) {
|
||||
switch (type) {
|
||||
case RegisterType::Input: return "v";
|
||||
case RegisterType::Output: return "o";
|
||||
case RegisterType::Temporary: return "r";
|
||||
case RegisterType::FloatUniform: return "c";
|
||||
case RegisterType::IntUniform: return "i";
|
||||
case RegisterType::BoolUniform: return "b";
|
||||
case RegisterType::ConditionalCode: return "cc";
|
||||
case RegisterType::Unknown: return "u";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
struct SourceRegister {
|
||||
SourceRegister() = default;
|
||||
|
||||
SourceRegister(uint32_t value) {
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
RegisterType GetRegisterType() const {
|
||||
if (value < 0x10)
|
||||
return RegisterType::Input;
|
||||
else if (value < 0x20)
|
||||
return RegisterType::Temporary;
|
||||
else
|
||||
return RegisterType::FloatUniform;
|
||||
}
|
||||
|
||||
int GetIndex() const {
|
||||
if (GetRegisterType() == RegisterType::Input)
|
||||
return value;
|
||||
else if (GetRegisterType() == RegisterType::Temporary)
|
||||
return value - 0x10;
|
||||
else if (GetRegisterType() == RegisterType::FloatUniform)
|
||||
return value - 0x20;
|
||||
}
|
||||
|
||||
static const SourceRegister FromTypeAndIndex(RegisterType type, int index) {
|
||||
SourceRegister reg;
|
||||
if (type == RegisterType::Input)
|
||||
reg.value = index;
|
||||
else if (type == RegisterType::Temporary)
|
||||
reg.value = index + 0x10;
|
||||
else if (type == RegisterType::FloatUniform)
|
||||
reg.value = index + 0x20;
|
||||
else {
|
||||
// TODO: Should throw an exception or something.
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
static const SourceRegister MakeInput(int index) {
|
||||
return FromTypeAndIndex(RegisterType::Input, index);
|
||||
}
|
||||
|
||||
static const SourceRegister MakeTemporary(int index) {
|
||||
return FromTypeAndIndex(RegisterType::Temporary, index);
|
||||
}
|
||||
|
||||
static const SourceRegister MakeFloat(int index) {
|
||||
return FromTypeAndIndex(RegisterType::FloatUniform, index);
|
||||
}
|
||||
|
||||
std::string GetName() const {
|
||||
std::stringstream ss;
|
||||
ss << GetRegisterName(GetRegisterType()) << GetIndex();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
operator uint32_t() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
decltype(uint32_t{} - T{}) operator -(const T& oth) const {
|
||||
return value - oth;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
decltype(uint32_t{} & T{}) operator &(const T& oth) const {
|
||||
return value & oth;
|
||||
}
|
||||
|
||||
uint32_t operator &(const SourceRegister& oth) const {
|
||||
return value & oth.value;
|
||||
}
|
||||
|
||||
uint32_t operator ~() const {
|
||||
return ~value;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
struct DestRegister {
|
||||
DestRegister() = default;
|
||||
|
||||
DestRegister(uint32_t value) {
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
RegisterType GetRegisterType() const {
|
||||
if (value < 0x10)
|
||||
return RegisterType::Output;
|
||||
else
|
||||
return RegisterType::Temporary;
|
||||
}
|
||||
|
||||
int GetIndex() const {
|
||||
if (GetRegisterType() == RegisterType::Output)
|
||||
return value;
|
||||
else if (GetRegisterType() == RegisterType::Temporary)
|
||||
return value - 0x10;
|
||||
else // if (GetRegisterType() == RegisterType::FloatUniform)
|
||||
// TODO: This will lead to negative returned values...
|
||||
return value - 0x20;
|
||||
}
|
||||
|
||||
static const DestRegister FromTypeAndIndex(RegisterType type, int index) {
|
||||
DestRegister reg;
|
||||
if (type == RegisterType::Output)
|
||||
reg.value = index;
|
||||
else if (type == RegisterType::Temporary)
|
||||
reg.value = index + 0x10;
|
||||
else if (type == RegisterType::FloatUniform) // TODO: Wait what? These shouldn't be writable..
|
||||
reg.value = index + 0x20;
|
||||
else {
|
||||
// TODO: Should throw an exception or something.
|
||||
}
|
||||
return reg;
|
||||
}
|
||||
|
||||
static const DestRegister MakeOutput(int index) {
|
||||
return FromTypeAndIndex(RegisterType::Output, index);
|
||||
}
|
||||
|
||||
static const DestRegister MakeTemporary(int index) {
|
||||
return FromTypeAndIndex(RegisterType::Temporary, index);
|
||||
}
|
||||
|
||||
std::string GetName() const {
|
||||
std::stringstream ss;
|
||||
ss << GetRegisterName(GetRegisterType()) << GetIndex();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
operator uint32_t() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
decltype(uint32_t{} - T{}) operator -(const T& oth) const {
|
||||
return value - oth;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
decltype(uint32_t{} & T{}) operator &(const T& oth) const {
|
||||
return value & oth;
|
||||
}
|
||||
|
||||
uint32_t operator &(const DestRegister& oth) const {
|
||||
return value & oth.value;
|
||||
}
|
||||
|
||||
uint32_t operator ~() const {
|
||||
return ~value;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
struct OpCode {
|
||||
enum class Id : uint32_t {
|
||||
ADD = 0x00,
|
||||
DP3 = 0x01,
|
||||
DP4 = 0x02,
|
||||
DPH = 0x03, // Dot product of Vec4 and Vec3; the Vec3 is made into
|
||||
// a Vec4 by appending 1.0 as the fourth component
|
||||
DST = 0x04, // Distance, same as in vs_3_0
|
||||
EX2 = 0x05, // Base-2 exponential
|
||||
LG2 = 0x06, // Base-2 logarithm
|
||||
LIT = 0x07, // Clamp for lighting
|
||||
MUL = 0x08,
|
||||
SGE = 0x09, // Set to 1.0 if SRC1 is greater or equal to SRC2
|
||||
SLT = 0x0A, // Set to 1.0 if SRC1 is less than SRC2
|
||||
FLR = 0x0B,
|
||||
MAX = 0x0C,
|
||||
MIN = 0x0D,
|
||||
RCP = 0x0E, // Reciprocal
|
||||
RSQ = 0x0F, // Reciprocal of square root
|
||||
|
||||
MOVA = 0x12, // Move to Address Register
|
||||
MOV = 0x13,
|
||||
|
||||
DPHI = 0x18,
|
||||
DSTI = 0x19,
|
||||
SGEI = 0x1A,
|
||||
SLTI = 0x1B,
|
||||
|
||||
BREAK = 0x20,
|
||||
NOP = 0x21,
|
||||
END = 0x22,
|
||||
BREAKC = 0x23,
|
||||
CALL = 0x24,
|
||||
CALLC = 0x25,
|
||||
CALLU = 0x26,
|
||||
IFU = 0x27,
|
||||
IFC = 0x28,
|
||||
LOOP = 0x29,
|
||||
EMIT = 0x2A,
|
||||
SETEMIT = 0x2B,
|
||||
JMPC = 0x2C,
|
||||
JMPU = 0x2D,
|
||||
CMP = 0x2E, // LSB opcode bit ignored
|
||||
|
||||
// lower 3 opcode bits ignored for these
|
||||
MADI = 0x30,
|
||||
MAD = 0x38, // lower 3 opcode bits ignored
|
||||
|
||||
// Pseudo-instructions, used internally by the assembler
|
||||
PSEUDO_INSTRUCTION_START = 0x40,
|
||||
|
||||
GEN_IF = PSEUDO_INSTRUCTION_START, // Generic IF (IFC or IFU)
|
||||
ELSE,
|
||||
ENDIF,
|
||||
GEN_CALL, // Generic CALL (CALL, CALC, or CALLU)
|
||||
GEN_JMP, // Generic JMP (JMPC or JMPU)
|
||||
//RET, // Return from function (not supported yet)
|
||||
ENDLOOP,
|
||||
};
|
||||
|
||||
enum class Type {
|
||||
Trivial, // 3dbrew format 0
|
||||
Arithmetic, // 3dbrew format 1
|
||||
Conditional, // 3dbrew format 2
|
||||
UniformFlowControl, // 3dbrew format 3
|
||||
SetEmit, // 3dbrew format 4
|
||||
MultiplyAdd, // 3dbrew format 5
|
||||
Unknown
|
||||
};
|
||||
|
||||
struct Info {
|
||||
Type type;
|
||||
|
||||
// Arithmetic
|
||||
enum : uint32_t {
|
||||
OpDesc = 1,
|
||||
Src1 = 2,
|
||||
Src2 = 4,
|
||||
Idx = 8,
|
||||
Dest = 16,
|
||||
SrcInversed = 32,
|
||||
CompareOps = 64,
|
||||
MOVA = 128 | OpDesc | Src1 | Idx,
|
||||
OneArgument = OpDesc | Src1 | Idx | Dest,
|
||||
TwoArguments = OneArgument | Src2,
|
||||
Compare = OpDesc | Idx | Src1 | Src2 | CompareOps,
|
||||
};
|
||||
|
||||
// Flow Control
|
||||
enum : uint32_t {
|
||||
HasUniformIndex = 1,
|
||||
HasCondition = 2,
|
||||
HasExplicitDest = 4, // target code given explicitly and context-independently (contrary to e.g. BREAKC)
|
||||
HasFinishPoint = 8, // last instruction until returning to caller
|
||||
HasAlternative = 16, // has an "else" branch
|
||||
LOOP = 32,
|
||||
|
||||
BREAKC = HasCondition,
|
||||
|
||||
JMP = HasExplicitDest,
|
||||
JMPC = JMP | HasCondition,
|
||||
JMPU = JMP | HasUniformIndex,
|
||||
|
||||
CALL = JMP | HasFinishPoint,
|
||||
CALLC = CALL | HasCondition,
|
||||
CALLU = CALL | HasUniformIndex,
|
||||
IFU = CALLU | HasAlternative,
|
||||
IFC = CALLC | HasAlternative,
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
FullAndBool,
|
||||
SimpleAndInt,
|
||||
};
|
||||
|
||||
uint32_t subtype;
|
||||
|
||||
const char* name;
|
||||
|
||||
// TODO: Deprecate.
|
||||
size_t NumArguments() const {
|
||||
if (type == Type::Arithmetic) {
|
||||
if (subtype & Src2)
|
||||
return 3;
|
||||
else if (subtype & Src1)
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
OpCode() = default;
|
||||
|
||||
OpCode(Id value) {
|
||||
this->value = static_cast<uint32_t>(value);
|
||||
}
|
||||
|
||||
OpCode(uint32_t value) {
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
Id EffectiveOpCode() const {
|
||||
uint32_t op = static_cast<uint32_t>(value);
|
||||
if (static_cast<Id>(op & ~0x7) == Id::MAD)
|
||||
return Id::MAD;
|
||||
else if (static_cast<Id>(op & ~0x7) == Id::MADI)
|
||||
return Id::MADI;
|
||||
else if (static_cast<Id>(op & ~0x1) == Id::CMP)
|
||||
return Id::CMP;
|
||||
else
|
||||
return static_cast<Id>(value);
|
||||
}
|
||||
|
||||
const Info& GetInfo() const {
|
||||
#define unknown_instruction { OpCode::Type::Unknown, 0, "UNK" }
|
||||
static const OpCode::Info info_table[] = {
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "add" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "dp3" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "dp4" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "dph" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "dst" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::OneArgument, "exp" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::OneArgument, "log" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::OneArgument, "lit" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "mul" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "sge" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "slt" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::OneArgument, "flr" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "max" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::TwoArguments, "min" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::OneArgument, "rcp" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::OneArgument, "rsq" },
|
||||
unknown_instruction,
|
||||
unknown_instruction,
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::MOVA, "mova" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::OneArgument, "mov" },
|
||||
unknown_instruction,
|
||||
unknown_instruction,
|
||||
unknown_instruction,
|
||||
unknown_instruction,
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::TwoArguments | OpCode::Info::SrcInversed, "dphi" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::TwoArguments | OpCode::Info::SrcInversed, "dsti" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::TwoArguments | OpCode::Info::SrcInversed, "sgei" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::TwoArguments | OpCode::Info::SrcInversed, "slti" },
|
||||
unknown_instruction,
|
||||
unknown_instruction,
|
||||
unknown_instruction,
|
||||
unknown_instruction,
|
||||
{ OpCode::Type::Trivial, 0, "break" },
|
||||
{ OpCode::Type::Trivial, 0, "nop" },
|
||||
{ OpCode::Type::Trivial, 0, "end" },
|
||||
{ OpCode::Type::Conditional, OpCode::Info::BREAKC, "breakc" },
|
||||
{ OpCode::Type::Conditional, OpCode::Info::CALL, "call" },
|
||||
{ OpCode::Type::Conditional, OpCode::Info::CALLC, "callc" },
|
||||
{ OpCode::Type::UniformFlowControl, OpCode::Info::CALLU, "callu" },
|
||||
{ OpCode::Type::UniformFlowControl, OpCode::Info::IFU, "ifu" },
|
||||
{ OpCode::Type::Conditional, OpCode::Info::IFC, "ifc" },
|
||||
{ OpCode::Type::UniformFlowControl, OpCode::Info::LOOP, "loop" },
|
||||
{ OpCode::Type::Trivial, 0, "emit" },
|
||||
{ OpCode::Type::SetEmit, 0, "setemit" },
|
||||
{ OpCode::Type::Conditional, OpCode::Info::JMPC, "jmpc" },
|
||||
{ OpCode::Type::Conditional, OpCode::Info::JMPU, "jmpu" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::Compare, "cmp" },
|
||||
{ OpCode::Type::Arithmetic, OpCode::Info::Compare, "cmp" },
|
||||
{ OpCode::Type::MultiplyAdd, OpCode::Info::SrcInversed, "madi" },
|
||||
{ OpCode::Type::MultiplyAdd, OpCode::Info::SrcInversed, "madi" },
|
||||
{ OpCode::Type::MultiplyAdd, OpCode::Info::SrcInversed, "madi" },
|
||||
{ OpCode::Type::MultiplyAdd, OpCode::Info::SrcInversed, "madi" },
|
||||
{ OpCode::Type::MultiplyAdd, OpCode::Info::SrcInversed, "madi" },
|
||||
{ OpCode::Type::MultiplyAdd, OpCode::Info::SrcInversed, "madi" },
|
||||
{ OpCode::Type::MultiplyAdd, OpCode::Info::SrcInversed, "madi" },
|
||||
{ OpCode::Type::MultiplyAdd, OpCode::Info::SrcInversed, "madi" },
|
||||
{ OpCode::Type::MultiplyAdd, 0, "mad" },
|
||||
{ OpCode::Type::MultiplyAdd, 0, "mad" },
|
||||
{ OpCode::Type::MultiplyAdd, 0, "mad" },
|
||||
{ OpCode::Type::MultiplyAdd, 0, "mad" },
|
||||
{ OpCode::Type::MultiplyAdd, 0, "mad" },
|
||||
{ OpCode::Type::MultiplyAdd, 0, "mad" },
|
||||
{ OpCode::Type::MultiplyAdd, 0, "mad" },
|
||||
{ OpCode::Type::MultiplyAdd, 0, "mad" }
|
||||
};
|
||||
#undef unknown_instruction
|
||||
return info_table[value];
|
||||
}
|
||||
|
||||
operator Id() const {
|
||||
return static_cast<Id>(value);
|
||||
}
|
||||
|
||||
OpCode operator << (size_t bits) const {
|
||||
return value << bits;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
decltype(uint32_t{} - T{}) operator -(const T& oth) const {
|
||||
return value - oth;
|
||||
}
|
||||
|
||||
uint32_t operator &(const OpCode& oth) const {
|
||||
return value & oth.value;
|
||||
}
|
||||
|
||||
uint32_t operator ~() const {
|
||||
return ~value;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
} // namespace nihstro
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct make_unsigned<nihstro::SourceRegister> {
|
||||
using type = nihstro::SourceRegister;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct make_unsigned<nihstro::DestRegister> {
|
||||
using type = nihstro::DestRegister;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct make_unsigned<nihstro::OpCode> {
|
||||
using type = nihstro::OpCode;
|
||||
};
|
||||
}
|
||||
|
||||
namespace nihstro {
|
||||
|
||||
#pragma pack(1)
|
||||
union Instruction {
|
||||
Instruction& operator =(const Instruction& instr) {
|
||||
hex = instr.hex;
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint32_t hex;
|
||||
|
||||
BitField<0x1a, 0x6, OpCode> opcode;
|
||||
|
||||
|
||||
// General notes:
|
||||
//
|
||||
// When two input registers are used, one of them uses a 5-bit index while the other
|
||||
// one uses a 7-bit index. This is because at most one floating point uniform may be used
|
||||
// as an input.
|
||||
|
||||
|
||||
// Format used e.g. by arithmetic instructions and comparisons
|
||||
union Common { // TODO: Remove name
|
||||
BitField<0x00, 0x7, uint32_t> operand_desc_id;
|
||||
|
||||
const SourceRegister GetSrc1(bool is_inverted) const {
|
||||
if (!is_inverted) {
|
||||
return src1;
|
||||
} else {
|
||||
return src1i;
|
||||
}
|
||||
}
|
||||
|
||||
const SourceRegister GetSrc2(bool is_inverted) const {
|
||||
if (!is_inverted) {
|
||||
return src2;
|
||||
} else {
|
||||
return src2i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Source inputs may be reordered for certain instructions.
|
||||
* Use GetSrc1 and GetSrc2 instead to access the input register indices hence.
|
||||
*/
|
||||
BitField<0x07, 0x5, SourceRegister> src2;
|
||||
BitField<0x0c, 0x7, SourceRegister> src1;
|
||||
BitField<0x07, 0x7, SourceRegister> src2i;
|
||||
BitField<0x0e, 0x5, SourceRegister> src1i;
|
||||
|
||||
// Address register value is used for relative addressing of src1 / src2 (inverted)
|
||||
BitField<0x13, 0x2, uint32_t> address_register_index;
|
||||
|
||||
union CompareOpType { // TODO: Make nameless once MSVC supports it
|
||||
enum Op : uint32_t {
|
||||
Equal = 0,
|
||||
NotEqual = 1,
|
||||
LessThan = 2,
|
||||
LessEqual = 3,
|
||||
GreaterThan = 4,
|
||||
GreaterEqual = 5,
|
||||
Unk6 = 6,
|
||||
Unk7 = 7
|
||||
};
|
||||
|
||||
BitField<0x15, 0x3, Op> y;
|
||||
BitField<0x18, 0x3, Op> x;
|
||||
|
||||
const std::string ToString(Op op) const {
|
||||
switch (op) {
|
||||
case Equal: return "==";
|
||||
case NotEqual: return "!=";
|
||||
case LessThan: return "<";
|
||||
case LessEqual: return "<=";
|
||||
case GreaterThan: return ">";
|
||||
case GreaterEqual: return ">=";
|
||||
case Unk6: return "UNK6";
|
||||
case Unk7: return "UNK7";
|
||||
default: return "";
|
||||
};
|
||||
}
|
||||
} compare_op;
|
||||
|
||||
std::string AddressRegisterName() const {
|
||||
if (address_register_index == 0) return "";
|
||||
else if (address_register_index == 1) return "a0.x";
|
||||
else if (address_register_index == 2) return "a0.y";
|
||||
else /*if (address_register_index == 3)*/ return "aL";
|
||||
}
|
||||
|
||||
BitField<0x15, 0x5, DestRegister> dest;
|
||||
} common;
|
||||
|
||||
union FlowControlType { // TODO: Make nameless once MSVC supports it
|
||||
enum Op : uint32_t {
|
||||
Or = 0,
|
||||
And = 1,
|
||||
JustX = 2,
|
||||
JustY = 3
|
||||
};
|
||||
|
||||
BitField<0x00, 0x8, uint32_t> num_instructions;
|
||||
BitField<0x0a, 0xc, uint32_t> dest_offset;
|
||||
|
||||
BitField<0x16, 0x2, Op> op;
|
||||
BitField<0x16, 0x4, uint32_t> bool_uniform_id;
|
||||
BitField<0x16, 0x2, uint32_t> int_uniform_id; // TODO: Verify that only this many bits are used...
|
||||
|
||||
BitFlag<0x18, uint32_t> refy;
|
||||
BitFlag<0x19, uint32_t> refx;
|
||||
} flow_control;
|
||||
|
||||
union {
|
||||
const SourceRegister GetSrc1(bool is_inverted) const {
|
||||
// The inverted form for src1 is the same, this function is just here for consistency
|
||||
return src1;
|
||||
}
|
||||
|
||||
const SourceRegister GetSrc2(bool is_inverted) const {
|
||||
if (!is_inverted) {
|
||||
return src2;
|
||||
} else {
|
||||
return src2i;
|
||||
}
|
||||
}
|
||||
|
||||
const SourceRegister GetSrc3(bool is_inverted) const {
|
||||
if (!is_inverted) {
|
||||
return src3;
|
||||
} else {
|
||||
return src3i;
|
||||
}
|
||||
}
|
||||
|
||||
BitField<0x00, 0x5, uint32_t> operand_desc_id;
|
||||
|
||||
BitField<0x05, 0x5, SourceRegister> src3;
|
||||
BitField<0x0a, 0x7, SourceRegister> src2;
|
||||
BitField<0x11, 0x5, SourceRegister> src1;
|
||||
|
||||
BitField<0x05, 0x7, SourceRegister> src3i;
|
||||
BitField<0x0c, 0x5, SourceRegister> src2i;
|
||||
|
||||
// Address register value is used for relative addressing of src2 / src3 (inverted)
|
||||
BitField<0x16, 0x2, uint32_t> address_register_index;
|
||||
|
||||
std::string AddressRegisterName() const {
|
||||
if (address_register_index == 0) return "";
|
||||
else if (address_register_index == 1) return "a0.x";
|
||||
else if (address_register_index == 2) return "a0.y";
|
||||
else /*if (address_register_index == 3)*/ return "aL";
|
||||
}
|
||||
|
||||
BitField<0x18, 0x5, DestRegister> dest;
|
||||
} mad;
|
||||
|
||||
union {
|
||||
BitField<0x16, 1, uint32_t> winding;
|
||||
BitField<0x17, 1, uint32_t> prim_emit;
|
||||
BitField<0x18, 2, uint32_t> vertex_id;
|
||||
} setemit;
|
||||
};
|
||||
static_assert(sizeof(Instruction) == 0x4, "Incorrect structure size");
|
||||
static_assert(std::is_standard_layout<Instruction>::value, "Structure does not have standard layout");
|
||||
|
||||
union SwizzlePattern {
|
||||
SwizzlePattern& operator =(const SwizzlePattern& instr) {
|
||||
hex = instr.hex;
|
||||
return *this;
|
||||
}
|
||||
|
||||
uint32_t hex;
|
||||
|
||||
enum class Selector : uint32_t {
|
||||
x = 0,
|
||||
y = 1,
|
||||
z = 2,
|
||||
w = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the raw 8-bit selector for the specified (1-indexed) source register.
|
||||
*/
|
||||
unsigned GetRawSelector(unsigned src) const {
|
||||
if (src == 0 || src > 3)
|
||||
throw std::out_of_range("src needs to be between 1 and 3");
|
||||
|
||||
unsigned selectors[] = {
|
||||
src1_selector, src2_selector, src3_selector
|
||||
};
|
||||
return selectors[src - 1];
|
||||
}
|
||||
|
||||
Selector GetSelectorSrc1(int comp) const {
|
||||
Selector selectors[] = {
|
||||
src1_selector_0, src1_selector_1, src1_selector_2, src1_selector_3
|
||||
};
|
||||
return selectors[comp];
|
||||
}
|
||||
|
||||
Selector GetSelectorSrc2(int comp) const {
|
||||
Selector selectors[] = {
|
||||
src2_selector_0, src2_selector_1, src2_selector_2, src2_selector_3
|
||||
};
|
||||
return selectors[comp];
|
||||
}
|
||||
|
||||
Selector GetSelectorSrc3(int comp) const {
|
||||
Selector selectors[] = {
|
||||
src3_selector_0, src3_selector_1, src3_selector_2, src3_selector_3
|
||||
};
|
||||
return selectors[comp];
|
||||
}
|
||||
|
||||
void SetSelectorSrc1(int comp, Selector value) {
|
||||
if (comp == 0)
|
||||
src1_selector_0 = value;
|
||||
else if (comp == 1)
|
||||
src1_selector_1 = value;
|
||||
else if (comp == 2)
|
||||
src1_selector_2 = value;
|
||||
else if (comp == 3)
|
||||
src1_selector_3 = value;
|
||||
else
|
||||
throw std::out_of_range("comp needs to be smaller than 4");
|
||||
}
|
||||
|
||||
void SetSelectorSrc2(int comp, Selector value) {
|
||||
if (comp == 0)
|
||||
src2_selector_0 = value;
|
||||
else if (comp == 1)
|
||||
src2_selector_1 = value;
|
||||
else if (comp == 2)
|
||||
src2_selector_2 = value;
|
||||
else if (comp == 3)
|
||||
src2_selector_3 = value;
|
||||
else
|
||||
throw std::out_of_range("comp needs to be smaller than 4");
|
||||
}
|
||||
|
||||
void SetSelectorSrc3(int comp, Selector value) {
|
||||
if (comp == 0)
|
||||
src3_selector_0 = value;
|
||||
else if (comp == 1)
|
||||
src3_selector_1 = value;
|
||||
else if (comp == 2)
|
||||
src3_selector_2 = value;
|
||||
else if (comp == 3)
|
||||
src3_selector_3 = value;
|
||||
else
|
||||
throw std::out_of_range("comp needs to be smaller than 4");
|
||||
}
|
||||
|
||||
std::string SelectorToString(bool src2) const {
|
||||
std::map<Selector, std::string> map = {
|
||||
{ Selector::x, "x" },
|
||||
{ Selector::y, "y" },
|
||||
{ Selector::z, "z" },
|
||||
{ Selector::w, "w" }
|
||||
};
|
||||
std::string ret;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
ret += map.at(src2 ? GetSelectorSrc2(i) : GetSelectorSrc1(i));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool DestComponentEnabled(unsigned int i) const {
|
||||
return (dest_mask & (0x8 >> i)) != 0;
|
||||
}
|
||||
|
||||
void SetDestComponentEnabled(unsigned int i, bool enabled) {
|
||||
int mask = 0xffff & (0x8 >> i);
|
||||
dest_mask = (dest_mask & ~mask) | (enabled * mask);
|
||||
}
|
||||
|
||||
std::string DestMaskToString() const {
|
||||
std::string ret;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (!DestComponentEnabled(i))
|
||||
ret += "_";
|
||||
else
|
||||
ret += "xyzw"[i];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Components of "dest" that should be written to: LSB=dest.w, MSB=dest.x
|
||||
BitField< 0, 4, uint32_t> dest_mask;
|
||||
|
||||
BitFlag < 4, uint32_t> negate_src1;
|
||||
BitField< 5, 8, uint32_t> src1_selector;
|
||||
BitField< 5, 2, Selector> src1_selector_3;
|
||||
BitField< 7, 2, Selector> src1_selector_2;
|
||||
BitField< 9, 2, Selector> src1_selector_1;
|
||||
BitField<11, 2, Selector> src1_selector_0;
|
||||
|
||||
BitFlag <13, uint32_t> negate_src2;
|
||||
BitField<14, 8, uint32_t> src2_selector;
|
||||
BitField<14, 2, Selector> src2_selector_3;
|
||||
BitField<16, 2, Selector> src2_selector_2;
|
||||
BitField<18, 2, Selector> src2_selector_1;
|
||||
BitField<20, 2, Selector> src2_selector_0;
|
||||
|
||||
BitFlag <22, uint32_t> negate_src3;
|
||||
BitField<23, 8, uint32_t> src3_selector;
|
||||
BitField<23, 2, Selector> src3_selector_3;
|
||||
BitField<25, 2, Selector> src3_selector_2;
|
||||
BitField<27, 2, Selector> src3_selector_1;
|
||||
BitField<29, 2, Selector> src3_selector_0;
|
||||
};
|
||||
static_assert(sizeof(SwizzlePattern) == 0x4, "Incorrect structure size");
|
||||
|
||||
|
||||
#pragma pack()
|
||||
|
||||
} // namespace
|
||||
341
externals/nihstro/include/nihstro/source_tree.h
vendored
Normal file
341
externals/nihstro/include/nihstro/source_tree.h
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/variant/recursive_wrapper.hpp>
|
||||
|
||||
namespace nihstro {
|
||||
|
||||
struct SourceTreeIterator;
|
||||
|
||||
|
||||
struct Node;
|
||||
// SequenceContainer
|
||||
struct SourceTree {
|
||||
SourceTree() = default;
|
||||
SourceTree(const SourceTree& oth);
|
||||
|
||||
std::string code;
|
||||
|
||||
struct {
|
||||
std::string filename;
|
||||
} file_info;
|
||||
|
||||
SourceTree* parent = nullptr;
|
||||
|
||||
// ordered with respect to "position"
|
||||
std::list<Node> children;
|
||||
|
||||
SourceTreeIterator begin();
|
||||
SourceTreeIterator end();
|
||||
|
||||
// Attach the given tree, changing the child's owner to *this.
|
||||
SourceTree& Attach(SourceTree tree, std::string::difference_type offset);
|
||||
};
|
||||
|
||||
struct Node {
|
||||
SourceTree tree;
|
||||
|
||||
std::string::difference_type offset_within_parent; // within "code"
|
||||
};
|
||||
|
||||
inline SourceTree::SourceTree(const SourceTree& oth) : code(oth.code), file_info(oth.file_info), parent(oth.parent), children(oth.children) {
|
||||
for (auto& child : children)
|
||||
child.tree.parent = this;
|
||||
}
|
||||
|
||||
inline SourceTree& SourceTree::Attach(SourceTree tree, std::string::difference_type offset) {
|
||||
tree.parent = this;
|
||||
children.push_back(Node{tree, offset});
|
||||
return *this;
|
||||
}
|
||||
|
||||
// RandomAccessIterator
|
||||
struct SourceTreeIterator {
|
||||
using difference_type = std::string::iterator::difference_type;
|
||||
using reference = std::string::iterator::reference;
|
||||
using value_type = std::string::iterator::value_type;
|
||||
using pointer = std::string::iterator::pointer;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
SourceTreeIterator() {
|
||||
}
|
||||
|
||||
SourceTreeIterator(SourceTree& tree) : tree(&tree), position(tree.code.begin()), node_iterator(tree.children.begin()) {
|
||||
UpdateChildIterator();
|
||||
}
|
||||
|
||||
SourceTreeIterator(const SourceTreeIterator&) = default;
|
||||
|
||||
SourceTreeIterator& operator += (difference_type n) {
|
||||
if (n > 0) {
|
||||
while (n) {
|
||||
if (child_iterator) {
|
||||
auto remaining_to_child = node_iterator->offset_within_parent - (position - tree->code.begin());
|
||||
if (remaining_to_child >= n) {
|
||||
// If the next child is more than n steps away, increase position by n and return
|
||||
// TODO: Should we make sure that we don't end up out-of-bounds here?
|
||||
position += n;
|
||||
UpdateNodeIterator();
|
||||
break;
|
||||
} else {
|
||||
// Otherwise, move current position to the child if it isn't there already
|
||||
position += remaining_to_child;
|
||||
n -= remaining_to_child;
|
||||
UpdateNodeIterator();
|
||||
}
|
||||
|
||||
if (child_iterator->get().StepsRemaining() > n) {
|
||||
// If child is larger than n, advance child by n and return
|
||||
child_iterator->get() += n;
|
||||
break;
|
||||
} else {
|
||||
// else step out of the child and increment next child iterator by one
|
||||
n -= child_iterator->get().StepsRemaining();
|
||||
if (node_iterator != tree->children.end())
|
||||
node_iterator++;
|
||||
UpdateChildIterator();
|
||||
}
|
||||
} else {
|
||||
// TODO: Should we make sure that we don't end up out-of-bounds here?
|
||||
position += n;
|
||||
UpdateNodeIterator();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (n < 0) {
|
||||
// Reduce to n>0 case by starting from begin()
|
||||
n = (*this - tree->begin()) + n;
|
||||
*this = tree->begin() + n;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
SourceTreeIterator& operator -= (difference_type n) {
|
||||
*this += -n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
difference_type operator -(SourceTreeIterator it) const {
|
||||
return this->StepsGone() - it.StepsGone();
|
||||
}
|
||||
|
||||
bool operator < (const SourceTreeIterator& it) const {
|
||||
return std::distance(*this, it) > 0;
|
||||
}
|
||||
|
||||
bool operator <= (const SourceTreeIterator& it) const {
|
||||
return std::distance(*this, it) >= 0;
|
||||
}
|
||||
|
||||
bool operator > (const SourceTreeIterator& it) const {
|
||||
return !(*this <= it);
|
||||
}
|
||||
|
||||
bool operator >= (const SourceTreeIterator& it) const {
|
||||
return !(*this < it);
|
||||
}
|
||||
|
||||
bool operator == (const SourceTreeIterator& it) const {
|
||||
return (*this <= it) && !(*this < it);
|
||||
}
|
||||
|
||||
bool operator != (const SourceTreeIterator& it) const {
|
||||
return !(*this == it);
|
||||
}
|
||||
|
||||
reference operator* () {
|
||||
return (*this)[0];
|
||||
}
|
||||
|
||||
SourceTreeIterator operator++ () {
|
||||
*this += 1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SourceTreeIterator operator++ (int) {
|
||||
auto it = *this;
|
||||
*this += 1;
|
||||
return it;
|
||||
}
|
||||
|
||||
SourceTreeIterator operator +(difference_type n) const {
|
||||
SourceTreeIterator it2 = *this;
|
||||
it2 += n;
|
||||
return it2;
|
||||
}
|
||||
|
||||
SourceTreeIterator operator -(SourceTreeIterator::difference_type n) const {
|
||||
return *this + (-n);
|
||||
}
|
||||
|
||||
reference operator [] (difference_type n) {
|
||||
auto it = (*this + n);
|
||||
if (it.WithinChild())
|
||||
return it.child_iterator->get()[0];
|
||||
else return *it.position;
|
||||
}
|
||||
|
||||
// Get line number (one-based) within "tree"
|
||||
unsigned GetLineNumber() const {
|
||||
// Adding one for natural (i.e. one-based) line numbers
|
||||
return std::count(tree->code.begin(), position, '\n') + 1;
|
||||
}
|
||||
|
||||
// Get line number (one-based) within the tree of the current child
|
||||
unsigned GetCurrentLineNumber() const {
|
||||
if (WithinChild())
|
||||
return child_iterator->get().GetCurrentLineNumber();
|
||||
|
||||
return GetLineNumber();
|
||||
}
|
||||
|
||||
const std::string GetCurrentFilename() const {
|
||||
if (WithinChild())
|
||||
return child_iterator->get().GetCurrentFilename();
|
||||
|
||||
return tree->file_info.filename;
|
||||
}
|
||||
|
||||
SourceTreeIterator GetParentIterator(const SourceTree* reference_tree) const {
|
||||
if (tree == reference_tree) {
|
||||
return *this;
|
||||
} else {
|
||||
return child_iterator->get().GetParentIterator(reference_tree);
|
||||
}
|
||||
}
|
||||
|
||||
SourceTree* GetCurrentTree() {
|
||||
if (WithinChild())
|
||||
return child_iterator->get().GetCurrentTree();
|
||||
else
|
||||
return tree;
|
||||
}
|
||||
|
||||
private:
|
||||
difference_type StepsRemaining() const {
|
||||
return std::distance(*this, tree->end());
|
||||
}
|
||||
|
||||
difference_type StepsGone() const {
|
||||
auto it = tree->begin();
|
||||
|
||||
difference_type diff = 0;
|
||||
|
||||
// Advance reference iterator starting from the beginning until we reach *this,
|
||||
// making sure that both the main position and the child iterator match.
|
||||
while (it.position != position ||
|
||||
((bool)it.child_iterator ^ (bool)child_iterator) ||
|
||||
(it.child_iterator && child_iterator && it.child_iterator->get() != child_iterator->get())) {
|
||||
// Move to next child (if there is one), or abort if we reach the reference position
|
||||
if (it.child_iterator) {
|
||||
auto distance_to_child = std::min(it.node_iterator->offset_within_parent - (it.position -it.tree->code.begin() ), position - it.position);
|
||||
|
||||
// Move to child or this->position
|
||||
diff += distance_to_child;
|
||||
it.position += distance_to_child;
|
||||
|
||||
if (it.position - it.tree->code.begin() == it.node_iterator->offset_within_parent) {
|
||||
if (node_iterator != tree->children.end() && it.node_iterator == node_iterator) {
|
||||
return diff + (child_iterator->get() - it.child_iterator->get());
|
||||
} else {
|
||||
// Move out of child
|
||||
diff += it.child_iterator->get().StepsRemaining();
|
||||
}
|
||||
} else {
|
||||
// We moved to this->position => done
|
||||
return diff;
|
||||
}
|
||||
|
||||
// Move to next child
|
||||
if (it.node_iterator != it.tree->children.end()) {
|
||||
it.node_iterator++;
|
||||
it.UpdateChildIterator();
|
||||
}
|
||||
} else {
|
||||
// no child remaining, hence just move to the given position
|
||||
return diff + (position - it.position);
|
||||
}
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
bool WithinChild() const {
|
||||
return child_iterator && position - tree->code.begin() == node_iterator->offset_within_parent;
|
||||
}
|
||||
|
||||
void UpdateChildIterator() {
|
||||
if (node_iterator != tree->children.end())
|
||||
child_iterator = boost::recursive_wrapper<SourceTreeIterator>(node_iterator->tree);
|
||||
else
|
||||
child_iterator = boost::none;
|
||||
}
|
||||
|
||||
void UpdateNodeIterator() {
|
||||
// Move to the first node which is at the cursor or behind it
|
||||
while (node_iterator != tree->children.end() && node_iterator->offset_within_parent < std::distance(tree->code.begin(), position)) {
|
||||
node_iterator++;
|
||||
UpdateChildIterator();
|
||||
}
|
||||
}
|
||||
|
||||
SourceTree* tree;
|
||||
std::string::iterator position;
|
||||
|
||||
boost::optional<boost::recursive_wrapper<SourceTreeIterator>> child_iterator; // points to current or next child
|
||||
std::list<Node>::iterator node_iterator; // points to current or next node
|
||||
|
||||
friend struct SourceTree;
|
||||
};
|
||||
|
||||
inline SourceTreeIterator operator +(SourceTreeIterator::difference_type n, const SourceTreeIterator& it) {
|
||||
return it + n;
|
||||
}
|
||||
|
||||
inline SourceTreeIterator operator -(SourceTreeIterator::difference_type n, const SourceTreeIterator& it) {
|
||||
return it - n;
|
||||
}
|
||||
|
||||
inline SourceTreeIterator SourceTree::begin() {
|
||||
return SourceTreeIterator(*this);
|
||||
}
|
||||
|
||||
inline SourceTreeIterator SourceTree::end() {
|
||||
auto it = SourceTreeIterator(*this);
|
||||
it.position = code.end();
|
||||
it.node_iterator = children.end();
|
||||
it.child_iterator = boost::none;
|
||||
return it;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user