First Commit

This commit is contained in:
2025-02-06 22:24:29 +08:00
parent ed7df4c81e
commit 7539e6a53c
18116 changed files with 6181499 additions and 0 deletions

View 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

View 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

View 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

View 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

View 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;

View 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

View 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

View 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

View 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

View 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