<commit_before>/***************************************************************************
    copyright            : (C) 2010 by Alex Novichkov
    email                : novichko@atnet.ru

    copyright            : (C) 2006 by Lukáš Lalinský
    email                : lalinsky@gmail.com
                           (original WavPack implementation)
 ***************************************************************************/

/***************************************************************************
 *   This library is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License version   *
 *   2.1 as published by the Free Software Foundation.                     *
 *                                                                         *
 *   This library is distributed in the hope that it will be useful, but   *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 *   Lesser General Public License for more details.                       *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA         *
 *   02110-1301  USA                                                       *
 *                                                                         *
 *   Alternatively, this file is available under the Mozilla Public        *
 *   License Version 1.1.  You may obtain a copy of the License at         *
 *   http://www.mozilla.org/MPL/                                           *
 ***************************************************************************/

#include <tstring.h>
#include <tdebug.h>
#include <bitset>
#include "id3v2tag.h"
#include "apeproperties.h"
#include "apefile.h"

using namespace TagLib;

class APE::Properties::PropertiesPrivate
{
public:
  PropertiesPrivate(File *file, long streamLength) :
    length(0),
    bitrate(0),
    sampleRate(0),
    channels(0),
    version(0),
    bitsPerSample(0),
    sampleFrames(0),
    file(file),
    streamLength(streamLength) {}

  int length;
  int bitrate;
  int sampleRate;
  int channels;
  int version;
  int bitsPerSample;
  uint sampleFrames;
  File *file;
  long streamLength;
};

////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

APE::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
{
  d = new PropertiesPrivate(file, file->length());
  read();
}

APE::Properties::~Properties()
{
  delete d;
}

int APE::Properties::length() const
{
  return d->length;
}

int APE::Properties::bitrate() const
{
  return d->bitrate;
}

int APE::Properties::sampleRate() const
{
  return d->sampleRate;
}

int APE::Properties::channels() const
{
  return d->channels;
}

int APE::Properties::version() const
{
  return d->version;
}

int APE::Properties::bitsPerSample() const
{
  return d->bitsPerSample;
}

TagLib::uint APE::Properties::sampleFrames() const
{
  return d->sampleFrames;
}

////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

void APE::Properties::read()
{
  // First we are searching the descriptor
  long offset = findDescriptor();
  if(offset < 0)
    return;

  // Then we read the header common for all versions of APE
  d->file->seek(offset);
  ByteVector commonHeader = d->file->readBlock(6);
  if(!commonHeader.startsWith("MAC "))
    return;
  d->version = commonHeader.toUShort(4, false);

  if(d->version >= 3980) {
    analyzeCurrent();
  }
  else {
    analyzeOld();
  }
}

long APE::Properties::findDescriptor()
{
  long ID3v2Location = findID3v2();
  long ID3v2OriginalSize = 0;
  bool hasID3v2 = false;
  if(ID3v2Location >= 0) {
    ID3v2::Tag tag(d->file, ID3v2Location);
    ID3v2OriginalSize = tag.header()->completeTagSize();
    if(tag.header()->tagSize() > 0)
      hasID3v2 = true;
  }

  long offset = 0;
  if(hasID3v2)
    offset = d->file->find("MAC ", ID3v2Location + ID3v2OriginalSize);
  else
    offset = d->file->find("MAC ");

  if(offset < 0) {
    debug("APE::Properties::findDescriptor() -- APE descriptor not found");
    return -1;
  }

  return offset;
}

long APE::Properties::findID3v2()
{
  if(!d->file->isValid())
    return -1;

  d->file->seek(0);

  if(d->file->readBlock(3) == ID3v2::Header::fileIdentifier())
    return 0;

  return -1;
}

void APE::Properties::analyzeCurrent()
{
  // Read the descriptor
  d->file->seek(2, File::Current);
  ByteVector descriptor = d->file->readBlock(44);
  const uint descriptorBytes = descriptor.toUInt(0, false);

  if ((descriptorBytes - 52) > 0)
    d->file->seek(descriptorBytes - 52, File::Current);

  // Read the header
  ByteVector header = d->file->readBlock(24);

  // Get the APE info
  d->channels      = header.toShort(18, false);
  d->sampleRate    = header.toUInt(20, false);
  d->bitsPerSample = header.toShort(16, false);
  //d->compressionLevel =

  const uint totalFrames      = header.toUInt(12, false);
  const uint blocksPerFrame   = header.toUInt(4, false);
  const uint finalFrameBlocks = header.toUInt(8, false);
  d->sampleFrames = totalFrames > 0 ? (totalFrames -  1) * blocksPerFrame + finalFrameBlocks : 0;
  d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0;
  d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
}

void APE::Properties::analyzeOld()
{
  ByteVector header = d->file->readBlock(26);
  const uint totalFrames = header.toUInt(18, false);

  // Fail on 0 length APE files (catches non-finalized APE files)
  if(totalFrames == 0)
    return;

  const short compressionLevel = header.toShort(0, false);
  uint blocksPerFrame;
  if(d->version >= 3950)
    blocksPerFrame = 73728 * 4;
  else if(d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000))
    blocksPerFrame = 73728;
  else
    blocksPerFrame = 9216;
  d->channels   = header.toShort(4, false);
  d->sampleRate = header.toUInt(6, false);
  const uint finalFrameBlocks = header.toUInt(22, false);
  const uint totalBlocks
    = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
  d->length = totalBlocks / d->sampleRate;
  d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
}

<commit_msg>Fix a division by zero error when parsing an APE file.<commit_after>/***************************************************************************
    copyright            : (C) 2010 by Alex Novichkov
    email                : novichko@atnet.ru

    copyright            : (C) 2006 by Lukáš Lalinský
    email                : lalinsky@gmail.com
                           (original WavPack implementation)
 ***************************************************************************/

/***************************************************************************
 *   This library is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU Lesser General Public License version   *
 *   2.1 as published by the Free Software Foundation.                     *
 *                                                                         *
 *   This library is distributed in the hope that it will be useful, but   *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 *   Lesser General Public License for more details.                       *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this library; if not, write to the Free Software   *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA         *
 *   02110-1301  USA                                                       *
 *                                                                         *
 *   Alternatively, this file is available under the Mozilla Public        *
 *   License Version 1.1.  You may obtain a copy of the License at         *
 *   http://www.mozilla.org/MPL/                                           *
 ***************************************************************************/

#include <tstring.h>
#include <tdebug.h>
#include <bitset>
#include "id3v2tag.h"
#include "apeproperties.h"
#include "apefile.h"

using namespace TagLib;

class APE::Properties::PropertiesPrivate
{
public:
  PropertiesPrivate(File *file, long streamLength) :
    length(0),
    bitrate(0),
    sampleRate(0),
    channels(0),
    version(0),
    bitsPerSample(0),
    sampleFrames(0),
    file(file),
    streamLength(streamLength) {}

  int length;
  int bitrate;
  int sampleRate;
  int channels;
  int version;
  int bitsPerSample;
  uint sampleFrames;
  File *file;
  long streamLength;
};

////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////

APE::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
{
  d = new PropertiesPrivate(file, file->length());
  read();
}

APE::Properties::~Properties()
{
  delete d;
}

int APE::Properties::length() const
{
  return d->length;
}

int APE::Properties::bitrate() const
{
  return d->bitrate;
}

int APE::Properties::sampleRate() const
{
  return d->sampleRate;
}

int APE::Properties::channels() const
{
  return d->channels;
}

int APE::Properties::version() const
{
  return d->version;
}

int APE::Properties::bitsPerSample() const
{
  return d->bitsPerSample;
}

TagLib::uint APE::Properties::sampleFrames() const
{
  return d->sampleFrames;
}

////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////

void APE::Properties::read()
{
  // First we are searching the descriptor
  long offset = findDescriptor();
  if(offset < 0)
    return;

  // Then we read the header common for all versions of APE
  d->file->seek(offset);
  ByteVector commonHeader = d->file->readBlock(6);
  if(!commonHeader.startsWith("MAC "))
    return;
  d->version = commonHeader.toUShort(4, false);

  if(d->version >= 3980) {
    analyzeCurrent();
  }
  else {
    analyzeOld();
  }
}

long APE::Properties::findDescriptor()
{
  long ID3v2Location = findID3v2();
  long ID3v2OriginalSize = 0;
  bool hasID3v2 = false;
  if(ID3v2Location >= 0) {
    ID3v2::Tag tag(d->file, ID3v2Location);
    ID3v2OriginalSize = tag.header()->completeTagSize();
    if(tag.header()->tagSize() > 0)
      hasID3v2 = true;
  }

  long offset = 0;
  if(hasID3v2)
    offset = d->file->find("MAC ", ID3v2Location + ID3v2OriginalSize);
  else
    offset = d->file->find("MAC ");

  if(offset < 0) {
    debug("APE::Properties::findDescriptor() -- APE descriptor not found");
    return -1;
  }

  return offset;
}

long APE::Properties::findID3v2()
{
  if(!d->file->isValid())
    return -1;

  d->file->seek(0);

  if(d->file->readBlock(3) == ID3v2::Header::fileIdentifier())
    return 0;

  return -1;
}

void APE::Properties::analyzeCurrent()
{
  // Read the descriptor
  d->file->seek(2, File::Current);
  ByteVector descriptor = d->file->readBlock(44);
  const uint descriptorBytes = descriptor.toUInt(0, false);

  if ((descriptorBytes - 52) > 0)
    d->file->seek(descriptorBytes - 52, File::Current);

  // Read the header
  ByteVector header = d->file->readBlock(24);

  // Get the APE info
  d->channels      = header.toShort(18, false);
  d->sampleRate    = header.toUInt(20, false);
  d->bitsPerSample = header.toShort(16, false);
  //d->compressionLevel =

  const uint totalFrames      = header.toUInt(12, false);
  const uint blocksPerFrame   = header.toUInt(4, false);
  const uint finalFrameBlocks = header.toUInt(8, false);
  d->sampleFrames = totalFrames > 0 ? (totalFrames -  1) * blocksPerFrame + finalFrameBlocks : 0;
  d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0;
  d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
}

void APE::Properties::analyzeOld()
{
  ByteVector header = d->file->readBlock(26);
  const uint totalFrames = header.toUInt(18, false);

  // Fail on 0 length APE files (catches non-finalized APE files)
  if(totalFrames == 0)
    return;

  const short compressionLevel = header.toShort(0, false);
  uint blocksPerFrame;
  if(d->version >= 3950)
    blocksPerFrame = 73728 * 4;
  else if(d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000))
    blocksPerFrame = 73728;
  else
    blocksPerFrame = 9216;

  d->channels   = header.toShort(4, false);
  d->sampleRate = header.toUInt(6, false);

  const uint finalFrameBlocks = header.toUInt(22, false);

  uint totalBlocks = 0;
  if(totalFrames > 0)
    totalBlocks = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;

  if(d->sampleRate > 0)
    d->length = totalBlocks / d->sampleRate;

  if(d->length > 0)
    d->bitrate = ((d->streamLength * 8L) / d->length) / 1000;
}

<|endoftext|>


<commit_before>#include "stdafx.h"
#include "rvdbg.h"

BOOLEAN tDSend;
CRITICAL_SECTION repr;
CONDITION_VARIABLE reprcondition;

static void HandleSSE()
{
	if (r_registers.bxmm0 == 1)
		__asm movsd xmm0, r_registers.dxmm0;
	else if (r_registers.bxmm0 == 2)
		__asm movss xmm0, r_registers.xmm0;

	if (r_registers.bxmm1)
		__asm movsd xmm1, r_registers.dxmm1;
	else if (r_registers.bxmm1 == 2)
		__asm movss xmm1, r_registers.xmm1;

	if (r_registers.bxmm2)
		__asm movsd xmm2, r_registers.dxmm2;
	else if (r_registers.bxmm2 == 2)
		__asm movss xmm2, r_registers.xmm2;

	if (r_registers.bxmm3)
		__asm movsd xmm3, r_registers.dxmm3;
	else if (r_registers.bxmm3 == 2)
		__asm movss xmm3, r_registers.xmm3;

	if (r_registers.bxmm4)
		__asm movsd xmm4, r_registers.dxmm4;
	else if (r_registers.bxmm4 == 2)
		__asm movss xmm4, r_registers.xmm4;

	if (r_registers.bxmm5)
		__asm movsd xmm5, r_registers.dxmm5;
	else if (r_registers.bxmm5 == 2)
		__asm movss xmm5, r_registers.xmm5;

	if (r_registers.bxmm6)
		__asm movsd xmm6, r_registers.dxmm6;
	else if (r_registers.bxmm6 == 2)
		__asm movss xmm6, r_registers.xmm6;

	if (r_registers.bxmm7)
		__asm movsd xmm7, r_registers.dxmm7;
	else if (r_registers.bxmm7 == 2)
		__asm movss xmm7, r_registers.xmm7;
	r_registers.bxmm0 = 0;
	r_registers.bxmm1 = 0;
	r_registers.bxmm2 = 0;
	r_registers.bxmm3 = 0;
	r_registers.bxmm4 = 0;
	r_registers.bxmm5 = 0;
	r_registers.bxmm6 = 0;
	r_registers.bxmm7 = 0;
}

static PVOID CallChain()
{
	size_t ExceptionElement = SearchSector(Sector, 128, ExceptionComparator);

	if (ExceptionElement > 128)
	{
		if (ExceptionMode == 2)
		{
			SuspendThreads(GetCurrentProcessId(), GetCurrentThreadId());
			DWORD swap_ad = SwapAccess(AccessException, AccessException);

			if (!swap_ad)
			{
				ChunkExecutable = FALSE;
				return NULL;
			}

			Swaps.push_back((PVOID)swap_ad);
			return (PVOID)swap_ad;
		}
		return NULL;
	}

	SuspendThreads(GetCurrentProcessId(), GetCurrentThreadId());
	Debugger = TRUE;
	tDSend = TRUE;

	for (size_t iterator = 0; iterator < sizeof(Threads); iterator++)
	{
		if (Threads[iterator] != NULL)
			ResumeThread(Threads[iterator]);
	}

	Sector[ExceptionElement].ExceptionCode = ExceptionCode;
	PVOID ReturnException = HandleException(Sector[ExceptionElement], Sector[ExceptionElement].ModuleName);
	r_registers.eip = ReturnException;

	Sector[ExceptionElement].Thread = GetCurrentThread();
	Sector[ExceptionElement].IsAEHPresent = TRUE;
	Sector[ExceptionElement].ReturnAddress = r_registers.ReturnAddress;

	CurrentPool = Sector[ExceptionElement];

	EnterCriticalSection(&RunLock);
	SleepConditionVariableCS(&Runnable, &RunLock, INFINITE);
	LeaveCriticalSection(&RunLock);

	ResumeThreads(GetCurrentProcessId(), GetCurrentThreadId());
	UnlockSector(Sector, ExceptionElement);
	return r_registers.eip;
}

static __declspec(naked) VOID NTAPI KiUserExceptionDispatcher(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT Context)
{
	__asm
	{
		movss r_registers.xmm0, xmm0;
		movss r_registers.xmm1, xmm1;
		movss r_registers.xmm2, xmm2;
		movss r_registers.xmm3, xmm3;
		movss r_registers.xmm4, xmm4;
		movss r_registers.xmm5, xmm5;
		movss r_registers.xmm6, xmm6;
		movss r_registers.xmm7, xmm7;
		movsd r_registers.dxmm0, xmm0;
		movsd r_registers.dxmm1, xmm1;
		movsd r_registers.dxmm2, xmm2;
		movsd r_registers.dxmm3, xmm3;
		movsd r_registers.dxmm4, xmm4;
		movsd r_registers.dxmm5, xmm5;
		movsd r_registers.dxmm6, xmm6;
		movsd r_registers.dxmm7, xmm7;
		mov r_registers.eax, eax;
		mov r_registers.ebx, ebx;
		mov r_registers.ecx, ecx;
		mov r_registers.edx, edx;
		mov r_registers.esi, esi;
		mov r_registers.edi, edi;
		mov r_registers.ebp, ebp;

		mov eax, [esp + 0x11c]; // Reserved former esp
		mov r_registers.esp, eax;

		mov eax, [eax];
		mov r_registers.ReturnAddress, eax;

		mov eax, [esp + 0x14]; // [esp + 0x14] contains the exception address
		mov[ExceptionComparator], eax; // move into the exception comparator, aka the address to be compared with the exception address
		mov eax, [esp + 0x08]; // [esp + 0x0C] contains the exception code
		mov[ExceptionCode], eax; // move into the ExceptionCode global.
		mov eax, [esp + 20]; // when access exceptions are enabled, move the accessed memoryh ere
		mov[AccessException], eax;
	}

	Decision = (PVOID)CallChain(); // Initiate the CallChain function

	if (!Decision) // if the decision is null, then jump back to the real dispatcher
	{
		__asm
		{
			mov eax, r_registers.eax;
			mov ecx, [esp + 04];
			mov ebx, [esp];
			jmp KiUserRealDispatcher;
		}
	}
	if (r_registers.SSESet == TRUE)
		HandleSSE();
	r_registers.SSESet = FALSE;
	__asm
	{
		mov eax, r_registers.eax;
		mov ebx, r_registers.ebx;
		mov ecx, r_registers.ecx;
		mov edx, r_registers.edx;
		mov esi, r_registers.esi;
		mov edi, r_registers.edi;
		mov esp, r_registers.esp; // [esp + 0x11c] contains stack initation information such as the return address, arguments, etc...
		jmp Decision; // jump to the catch block
	}

}

static void SetKiUser()
{
	KiUser = (PVOID)GetProcAddress(GetModuleHandleA("ntdll.dll"), "KiUserExceptionDispatcher"); // Hook, tampered exception dispatcher later
	KiUserRealDispatcher = (PVOID)GetProcAddress(GetModuleHandleA("ntdll.dll"), "KiUserExceptionDispatcher"); // If something fails, will jump back to the real dispatcher

	DWORD KiUserRealDispatcher2 = (DWORD)KiUserRealDispatcher + 8;
	DWORD KiUser2 = (DWORD)KiUser + 1;

	KiUser = (PVOID)KiUser2;
	KiUserRealDispatcher = (PVOID)KiUserRealDispatcher2;
}

static IMP_AT GetIAT(LPCSTR ModuleName)
{
	HMODULE mod = GetModuleHandleA(ModuleName);

	PIMAGE_DOS_HEADER img_dos_headers = (PIMAGE_DOS_HEADER)mod;
	PIMAGE_NT_HEADERS img_nt_headers = (PIMAGE_NT_HEADERS)((BYTE*)img_dos_headers + img_dos_headers->e_lfanew);

	PIMAGE_IMPORT_DESCRIPTOR img_import_desc = (PIMAGE_IMPORT_DESCRIPTOR)((BYTE*)img_dos_headers +
		img_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

	DWORD IATSize = (img_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size * 4);

	IMP_AT Retn;
	Retn.Size = IATSize;
	Retn.Address = (PVOID)((DWORD)mod + img_import_desc->FirstThunk - 0x1DC);
	return Retn;
}

static void SetImportAddressTable(const char* ModuleName)
{
	DWORD OldProtect;
	MEMORY_BASIC_INFORMATION inf;
	IMP_AT CopyModule = GetIAT(0);
	IMP_AT SelfModule = GetIAT(ModuleName);
	printf("_iat: %p\n", CopyModule.Address);
	printf("iat: %p\n", SelfModule.Address);
	VirtualProtect(SelfModule.Address, 1, PAGE_EXECUTE_READWRITE, &OldProtect);
	printf("Error1: %d\n", GetLastError());
	VirtualQuery(SelfModule.Address, &inf, sizeof(inf));
	memcpy(SelfModule.Address, CopyModule.Address, inf.RegionSize);
	VirtualProtect(SelfModule.Address, 1, OldProtect, &OldProtect);
}

int WaitOptModule(const char* OptModuleName)
{
	if (!UseModule)
		return -1;
	volatile PVOID ModPtr = NULL;
	while (!ModPtr)
		ModPtr = (PVOID)GetModuleHandleA(OptModuleName);
	SetImportAddressTable(OptModuleName);
	return 0;
}

void SetModule(BOOLEAN use)
{
	UseModule = use;
}

int AssignThread(HANDLE Thread)
{
	for (size_t iterator = 0; iterator < sizeof(Threads); iterator++)
	{
		if (Threads[iterator] == NULL)
		{
			Threads[iterator] = Thread;
			return iterator;
		}
	}
	return -1;
}

void RemoveThread(HANDLE Thread)
{
	for (size_t iterator = 0; iterator < sizeof(Threads); iterator++)
	{
		if (Threads[iterator] == Thread)
		{
			CloseHandle(Threads[iterator]);
			Threads[iterator] = NULL;
			return;
		}
	}
}

void AttachRVDbg()
{
	InitializeConditionVariable(&Runnable);
	InitializeCriticalSection(&RunLock);
	SetKiUser();
	HookFunction(KiUser, (PVOID)KiUserExceptionDispatcher, "ntdll.dll:KiUserExceptionDispatcher");
}

void DetachRVDbg()
{
	UnhookFunction(KiUser, (PVOID)KiUserExceptionDispatcher, "ntdll.dll:KiUserExceptionDispatcher");
}

void ContinueDebugger()
{
	WakeConditionVariable(&Runnable);
}

BOOLEAN IsAEHPresent()
{
	return CurrentPool.IsAEHPresent;
}

void SetRegister(DWORD Register, DWORD Value)
{
	switch (Register)
	{
	case GPRegisters::EAX:
		r_registers.eax = Value;
		return;
	case GPRegisters::EBX:
		r_registers.ebx = Value;
		return;
	case GPRegisters::ECX:
		r_registers.ecx = Value;
		return;
	case GPRegisters::EDX:
		r_registers.edx = Value;
		return;
	case GPRegisters::ESI:
		r_registers.esi = Value;
		return;
	case GPRegisters::EDI:
		r_registers.edi = Value;
		return;
	case GPRegisters::EBP:
		r_registers.ebp = Value;
		return;
	case GPRegisters::ESP:
		r_registers.esp = Value;
		return;
	case GPRegisters::EIP:
		r_registers.eip = (PVOID)Value;
	}
}

void SetRegisterFP(DWORD Register, BOOLEAN Precision, double Value)
{
	r_registers.SSESet = TRUE;
	switch (Register)
	{
	case SSERegisters::xmm0:
		if (Precision)
		{
			r_registers.bxmm0 = 1;
			r_registers.dxmm0 = Value;
			return;
		}
		r_registers.bxmm0 = 2;
		r_registers.xmm0 = (float)Value;
		return;
	case SSERegisters::xmm1:
		if (Precision)
		{
			r_registers.bxmm1 = 1;
			r_registers.dxmm1 = Value;
			return;
		}
		r_registers.bxmm1 = 2;
		r_registers.xmm1 = (float)Value;
		return;
	case SSERegisters::xmm2:
		if (Precision)
		{
			r_registers.bxmm2 = 1;
			r_registers.dxmm2 = Value;
			return;
		}
		r_registers.bxmm2 = 2;
		r_registers.xmm2 = (float)Value;
		return;
	case SSERegisters::xmm3:
		if (Precision)
		{
			r_registers.bxmm3 = 1;
			r_registers.dxmm3 = Value;
			return;
		}
		r_registers.bxmm3 = 2;
		r_registers.xmm3 = (float)Value;
		return;
	case SSERegisters::xmm4:
		if (Precision)
		{
			r_registers.bxmm4 = 1;
			r_registers.dxmm4 = Value;
			return;
		}
		r_registers.bxmm4 = 2;
		r_registers.xmm4 = (float)Value;
		return;
	case SSERegisters::xmm5:
		if (Precision)
		{
			r_registers.bxmm5 = 1;
			r_registers.dxmm5 = Value;
			return;
		}
		r_registers.bxmm5 = 2;
		r_registers.xmm5 = (float)Value;
		return;
	case SSERegisters::xmm6:
		if (Precision)
		{
			r_registers.bxmm6 = 1;
			r_registers.dxmm6 = Value;
			return;
		}
		r_registers.bxmm6 = 2;
		r_registers.xmm6 = (float)Value;
		return;
	case SSERegisters::xmm7:
		if (Precision)
		{
			r_registers.bxmm7 = 1;
			r_registers.dxmm7 = Value;
			return;
		}
		r_registers.bxmm7 = 2;
		r_registers.xmm7 = (float)Value;
		return;
	}
}

void SetExceptionMode(BOOLEAN lExceptionMode)
{
	ExceptionMode = lExceptionMode;
}

BOOLEAN GetExceptionMode()
{
	return ExceptionMode;
}

DWORD GetExceptionAddress()
{
	return CurrentPool.ExceptionAddress;
}

VirtualRegisters GetRegisters()
{
	return r_registers;
}

PoolSect GetPool()
{
	return CurrentPool;
}

PoolSect* GetSector()
{
	return Sector;
}

int GetSectorSize()
{
	return sizeof(Sector) / sizeof(PoolSect);
}

<commit_msg>Update rvdbg.cpp<commit_after>#include "stdafx.h"
#include "rvdbg.h"

BOOLEAN tDSend;
CRITICAL_SECTION repr;
CONDITION_VARIABLE reprcondition;

static void Dbg::HandleSSE()
{
	if (r_registers.bxmm0 == 1)
		__asm movsd xmm0, r_registers.dxmm0;
	else if (r_registers.bxmm0 == 2)
		__asm movss xmm0, r_registers.xmm0;

	if (r_registers.bxmm1)
		__asm movsd xmm1, r_registers.dxmm1;
	else if (r_registers.bxmm1 == 2)
		__asm movss xmm1, r_registers.xmm1;

	if (r_registers.bxmm2)
		__asm movsd xmm2, r_registers.dxmm2;
	else if (r_registers.bxmm2 == 2)
		__asm movss xmm2, r_registers.xmm2;

	if (r_registers.bxmm3)
		__asm movsd xmm3, r_registers.dxmm3;
	else if (r_registers.bxmm3 == 2)
		__asm movss xmm3, r_registers.xmm3;

	if (r_registers.bxmm4)
		__asm movsd xmm4, r_registers.dxmm4;
	else if (r_registers.bxmm4 == 2)
		__asm movss xmm4, r_registers.xmm4;

	if (r_registers.bxmm5)
		__asm movsd xmm5, r_registers.dxmm5;
	else if (r_registers.bxmm5 == 2)
		__asm movss xmm5, r_registers.xmm5;

	if (r_registers.bxmm6)
		__asm movsd xmm6, r_registers.dxmm6;
	else if (r_registers.bxmm6 == 2)
		__asm movss xmm6, r_registers.xmm6;

	if (r_registers.bxmm7)
		__asm movsd xmm7, r_registers.dxmm7;
	else if (r_registers.bxmm7 == 2)
		__asm movss xmm7, r_registers.xmm7;
	r_registers.bxmm0 = 0;
	r_registers.bxmm1 = 0;
	r_registers.bxmm2 = 0;
	r_registers.bxmm3 = 0;
	r_registers.bxmm4 = 0;
	r_registers.bxmm5 = 0;
	r_registers.bxmm6 = 0;
	r_registers.bxmm7 = 0;
}

static PVOID Dbg::CallChain()
{
	size_t ExceptionElement = SearchSector(Sector, 128, ExceptionComparator);

	if (ExceptionElement > 128)
	{
		if (ExceptionMode == 2)
		{
			SuspendThreads(GetCurrentProcessId(), GetCurrentThreadId());
			DWORD swap_ad = Dispatcher::SwapAccess(AccessException, AccessException);

			if (!swap_ad)
			{
				ChunkExecutable = FALSE;
				return NULL;
			}

			Swaps.push_back((PVOID)swap_ad);
			return (PVOID)swap_ad;
		}
		return NULL;
	}

	SuspendThreads(GetCurrentProcessId(), GetCurrentThreadId());
	Debugger = TRUE;
	tDSend = TRUE;
	WakeConditionVariable(&reprcondition);

	for (size_t iterator = 0; iterator < sizeof(Threads); iterator++)
	{
		if (Threads[iterator] != NULL)
			ResumeThread(Threads[iterator]);
	}

	Sector[ExceptionElement].ExceptionCode = ExceptionCode;
	PVOID ReturnException = Dispatcher::HandleException(Sector[ExceptionElement], Sector[ExceptionElement].ModuleName);
	r_registers.eip = ReturnException;

	Sector[ExceptionElement].Thread = GetCurrentThread();
	Sector[ExceptionElement].IsAEHPresent = TRUE;
	Sector[ExceptionElement].ReturnAddress = r_registers.ReturnAddress;

	CurrentPool = Sector[ExceptionElement];

	EnterCriticalSection(&RunLock);
	SleepConditionVariableCS(&Runnable, &RunLock, INFINITE);
	LeaveCriticalSection(&RunLock);

	ResumeThreads(GetCurrentProcessId(), GetCurrentThreadId());
	Dispatcher::UnlockSector(Sector, ExceptionElement);
	return r_registers.eip;
}

static __declspec(naked) VOID NTAPI KiUserExceptionDispatcher(PEXCEPTION_RECORD ExceptionRecord, PCONTEXT Context)
{
	__asm
	{
		movss r_registers.xmm0, xmm0;
		movss r_registers.xmm1, xmm1;
		movss r_registers.xmm2, xmm2;
		movss r_registers.xmm3, xmm3;
		movss r_registers.xmm4, xmm4;
		movss r_registers.xmm5, xmm5;
		movss r_registers.xmm6, xmm6;
		movss r_registers.xmm7, xmm7;
		movsd r_registers.dxmm0, xmm0;
		movsd r_registers.dxmm1, xmm1;
		movsd r_registers.dxmm2, xmm2;
		movsd r_registers.dxmm3, xmm3;
		movsd r_registers.dxmm4, xmm4;
		movsd r_registers.dxmm5, xmm5;
		movsd r_registers.dxmm6, xmm6;
		movsd r_registers.dxmm7, xmm7;
		mov r_registers.eax, eax;
		mov r_registers.ebx, ebx;
		mov r_registers.ecx, ecx;
		mov r_registers.edx, edx;
		mov r_registers.esi, esi;
		mov r_registers.edi, edi;
		mov r_registers.ebp, ebp;

		mov eax, [esp + 0x11c]; // Reserved former esp
		mov r_registers.esp, eax;

		mov eax, [eax];
		mov r_registers.ReturnAddress, eax;

		mov eax, [esp + 0x14]; // [esp + 0x14] contains the exception address
		mov[ExceptionComparator], eax; // move into the exception comparator, aka the address to be compared with the exception address
		mov eax, [esp + 0x08]; // [esp + 0x0C] contains the exception code
		mov[ExceptionCode], eax; // move into the ExceptionCode global.
		mov eax, [esp + 20]; // when access exceptions are enabled, move the accessed memoryh ere
		mov[AccessException], eax;
	}

	Decision = (PVOID)Dbg::CallChain(); // Initiate the CallChain function

	if (!Decision) // if the decision is null, then jump back to the real dispatcher
	{
		__asm
		{
			mov eax, r_registers.eax;
			mov ecx, [esp + 04];
			mov ebx, [esp];
			jmp KiUserRealDispatcher;
		}
	}
	if (r_registers.SSESet == TRUE)
		Dbg::HandleSSE();
	r_registers.SSESet = FALSE;
	__asm
	{
		mov eax, r_registers.eax;
		mov ebx, r_registers.ebx;
		mov ecx, r_registers.ecx;
		mov edx, r_registers.edx;
		mov esi, r_registers.esi;
		mov edi, r_registers.edi;
		mov esp, r_registers.esp; // [esp + 0x11c] contains stack initation information such as the return address, arguments, etc...
		jmp Decision; // jump to the catch block
	}

}

static void Dbg::SetKiUser()
{
	KiUser = (PVOID)GetProcAddress(GetModuleHandleA("ntdll.dll"), "KiUserExceptionDispatcher"); // Hook, tampered exception dispatcher later
	KiUserRealDispatcher = (PVOID)GetProcAddress(GetModuleHandleA("ntdll.dll"), "KiUserExceptionDispatcher"); // If something fails, will jump back to the real dispatcher

	DWORD KiUserRealDispatcher2 = (DWORD)KiUserRealDispatcher + 8;
	DWORD KiUser2 = (DWORD)KiUser + 1;

	KiUser = (PVOID)KiUser2;
	KiUserRealDispatcher = (PVOID)KiUserRealDispatcher2;
}

static Dbg::IMP_AT Dbg::GetIAT(LPCSTR ModuleName)
{
	HMODULE mod = GetModuleHandleA(ModuleName);

	PIMAGE_DOS_HEADER img_dos_headers = (PIMAGE_DOS_HEADER)mod;
	PIMAGE_NT_HEADERS img_nt_headers = (PIMAGE_NT_HEADERS)((BYTE*)img_dos_headers + img_dos_headers->e_lfanew);

	PIMAGE_IMPORT_DESCRIPTOR img_import_desc = (PIMAGE_IMPORT_DESCRIPTOR)((BYTE*)img_dos_headers +
		img_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

	DWORD IATSize = (img_nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size * 4);

	IMP_AT Retn;
	Retn.Size = IATSize;
	Retn.Address = (PVOID)((DWORD)mod + img_import_desc->FirstThunk - 0x1DC);
	return Retn;
}

int Dbg::WaitOptModule(const char* OriginalModuleName, const char* OptModuleName)
{
	if (!UseModule)
		return -1;
	volatile PVOID ModPtr = NULL;
	while (!ModPtr)
		ModPtr = (PVOID)GetModuleHandleA(OptModuleName);
	IATResolver::ResolveIAT(OriginalModuleName, OptModuleName);
	return 0;
}

void Dbg::SetModule(BOOLEAN use)
{
	UseModule = use;
}

int Dbg::AssignThread(HANDLE Thread)
{
	for (size_t iterator = 0; iterator < sizeof(Threads); iterator++)
	{
		if (Threads[iterator] == NULL)
		{
			Threads[iterator] = Thread;
			return iterator;
		}
	}
	return -1;
}

void Dbg::RemoveThread(HANDLE Thread)
{
	for (size_t iterator = 0; iterator < sizeof(Threads); iterator++)
	{
		if (Threads[iterator] == Thread)
		{
			CloseHandle(Threads[iterator]);
			Threads[iterator] = NULL;
			return;
		}
	}
}

void Dbg::AttachRVDbg()
{
	InitializeConditionVariable(&Runnable);
	InitializeCriticalSection(&RunLock);
	Dbg::SetKiUser();
	HookFunction(KiUser, (PVOID)KiUserExceptionDispatcher, "ntdll.dll:KiUserExceptionDispatcher");
}

void Dbg::DetachRVDbg()
{
	UnhookFunction(KiUser, (PVOID)KiUserExceptionDispatcher, "ntdll.dll:KiUserExceptionDispatcher");
}

void Dbg::ContinueDebugger()
{
	WakeConditionVariable(&Runnable);
}

BOOLEAN Dbg::IsAEHPresent()
{
	return CurrentPool.IsAEHPresent;
}

void Dbg::SetRegister(DWORD Register, DWORD Value)
{
	switch (Register)
	{
	case Dbg::GPRegisters::EAX:
		r_registers.eax = Value;
		return;
	case Dbg::GPRegisters::EBX:
		r_registers.ebx = Value;
		return;
	case Dbg::GPRegisters::ECX:
		r_registers.ecx = Value;
		return;
	case Dbg::GPRegisters::EDX:
		r_registers.edx = Value;
		return;
	case Dbg::GPRegisters::ESI:
		r_registers.esi = Value;
		return;
	case Dbg::GPRegisters::EDI:
		r_registers.edi = Value;
		return;
	case Dbg::GPRegisters::EBP:
		r_registers.ebp = Value;
		return;
	case Dbg::GPRegisters::ESP:
		r_registers.esp = Value;
		return;
	case Dbg::GPRegisters::EIP:
		r_registers.eip = (PVOID)Value;
	}
}

void Dbg::SetRegisterFP(DWORD Register, BOOLEAN Precision, double Value)
{
	r_registers.SSESet = TRUE;
	switch (Register)
	{
	case Dbg::SSERegisters::xmm0:
		if (Precision)
		{
			r_registers.bxmm0 = 1;
			r_registers.dxmm0 = Value;
			return;
		}
		r_registers.bxmm0 = 2;
		r_registers.xmm0 = (float)Value;
		return;
	case Dbg::SSERegisters::xmm1:
		if (Precision)
		{
			r_registers.bxmm1 = 1;
			r_registers.dxmm1 = Value;
			return;
		}
		r_registers.bxmm1 = 2;
		r_registers.xmm1 = (float)Value;
		return;
	case Dbg::SSERegisters::xmm2:
		if (Precision)
		{
			r_registers.bxmm2 = 1;
			r_registers.dxmm2 = Value;
			return;
		}
		r_registers.bxmm2 = 2;
		r_registers.xmm2 = (float)Value;
		return;
	case Dbg::SSERegisters::xmm3:
		if (Precision)
		{
			r_registers.bxmm3 = 1;
			r_registers.dxmm3 = Value;
			return;
		}
		r_registers.bxmm3 = 2;
		r_registers.xmm3 = (float)Value;
		return;
	case Dbg::SSERegisters::xmm4:
		if (Precision)
		{
			r_registers.bxmm4 = 1;
			r_registers.dxmm4 = Value;
			return;
		}
		r_registers.bxmm4 = 2;
		r_registers.xmm4 = (float)Value;
		return;
	case Dbg::SSERegisters::xmm5:
		if (Precision)
		{
			r_registers.bxmm5 = 1;
			r_registers.dxmm5 = Value;
			return;
		}
		r_registers.bxmm5 = 2;
		r_registers.xmm5 = (float)Value;
		return;
	case Dbg::SSERegisters::xmm6:
		if (Precision)
		{
			r_registers.bxmm6 = 1;
			r_registers.dxmm6 = Value;
			return;
		}
		r_registers.bxmm6 = 2;
		r_registers.xmm6 = (float)Value;
		return;
	case Dbg::SSERegisters::xmm7:
		if (Precision)
		{
			r_registers.bxmm7 = 1;
			r_registers.dxmm7 = Value;
			return;
		}
		r_registers.bxmm7 = 2;
		r_registers.xmm7 = (float)Value;
		return;
	}
}

void Dbg::SetExceptionMode(BOOLEAN lExceptionMode)
{
	ExceptionMode = lExceptionMode;
}

BOOLEAN Dbg::GetExceptionMode()
{
	return ExceptionMode;
}

DWORD Dbg::GetExceptionAddress()
{
	return CurrentPool.ExceptionAddress;
}

Dbg::VirtualRegisters Dbg::GetRegisters()
{
	return r_registers;
}

Dispatcher::PoolSect Dbg::GetPool()
{
	return CurrentPool;
}

Dispatcher::PoolSect* Dbg::GetSector()
{
	return Sector;
}

int Dbg::GetSectorSize()
{
	return sizeof(Sector) / sizeof(Dispatcher::PoolSect);
}

<|endoftext|>


<commit_before>#ifdef _WIN32

#include "win_fsnotifier.h"

using namespace std;

//
// WatchPoint
//

WatchPoint::WatchPoint(Server* server, const u16string& path, HANDLE directoryHandle) {
    this->server = server;
    this->path = path;
    this->buffer = (FILE_NOTIFY_INFORMATION*) malloc(EVENT_BUFFER_SIZE);
    ZeroMemory(&this->overlapped, sizeof(OVERLAPPED));
    this->overlapped.hEvent = this;
    this->directoryHandle = directoryHandle;
    this->status = WATCH_UNINITIALIZED;
}

WatchPoint::~WatchPoint() {
    free(buffer);
}

void WatchPoint::close() {
    BOOL ret = CancelIo(directoryHandle);
    if (!ret) {
        log_severe(server->getThreadEnv(), L"Couldn't cancel I/O %p for '%ls': %d", directoryHandle, path.c_str(), GetLastError());
    }
    ret = CloseHandle(directoryHandle);
    if (!ret) {
        log_severe(server->getThreadEnv(), L"Couldn't close handle %p for '%ls': %d", directoryHandle, path.c_str(), GetLastError());
    }
}

static void CALLBACK startWatchCallback(_In_ ULONG_PTR arg) {
    WatchPoint* watchPoint = (WatchPoint*) arg;
    watchPoint->listen();
}

int WatchPoint::awaitListeningStarted(HANDLE threadHandle) {
    unique_lock<mutex> lock(listenerMutex);
    QueueUserAPC(startWatchCallback, threadHandle, (ULONG_PTR) this);
    listenerStarted.wait(lock);
    return status;
}

void WatchPoint::listen() {
    BOOL success = ReadDirectoryChangesW(
        directoryHandle,        // handle to directory
        buffer,                 // read results buffer
        EVENT_BUFFER_SIZE,      // length of buffer
        TRUE,                   // include children
        EVENT_MASK,             // filter conditions
        NULL,                   // bytes returned
        &overlapped,            // overlapped buffer
        &handleEventCallback    // completion routine
    );

    unique_lock<mutex> lock(listenerMutex);
    if (success) {
        status = WATCH_LISTENING;
    } else {
        status = WATCH_FAILED_TO_LISTEN;
        log_warning(server->getThreadEnv(), L"Couldn't start watching %p for '%ls', error = %d", directoryHandle, path.c_str(), GetLastError());
        // TODO Error handling
    }
    listenerStarted.notify_all();
}

static void CALLBACK handleEventCallback(DWORD errorCode, DWORD bytesTransferred, LPOVERLAPPED overlapped) {
    WatchPoint* watchPoint = (WatchPoint*) overlapped->hEvent;
    watchPoint->handleEvent(errorCode, bytesTransferred);
}

void WatchPoint::handleEvent(DWORD errorCode, DWORD bytesTransferred) {
    status = WATCH_NOT_LISTENING;

    if (errorCode == ERROR_OPERATION_ABORTED) {
        log_info(server->getThreadEnv(), L"Finished watching '%ls'", path.c_str());
        status = WATCH_FINISHED;
        server->reportFinished(this);
        return;
    }

    if (bytesTransferred == 0) {
        // don't send dirty too much, everything is changed anyway
        // TODO Understand what this does
        // if (WaitForSingleObject(stopEventHandle, 500) == WAIT_OBJECT_0)
        //    break;

        // Got a buffer overflow => current changes lost => send INVALIDATE on root
        server->reportEvent(FILE_EVENT_INVALIDATE, path);
    } else {
        FILE_NOTIFY_INFORMATION* current = buffer;
        for (;;) {
            handlePathChanged(current);
            if (current->NextEntryOffset == 0) {
                break;
            }
            current = (FILE_NOTIFY_INFORMATION*) (((BYTE*) current) + current->NextEntryOffset);
        }
    }

    listen();
    if (status != WATCH_LISTENING) {
        server->reportFinished(this);
    }
}

void WatchPoint::handlePathChanged(FILE_NOTIFY_INFORMATION* info) {
    wstring changedPathW = wstring(info->FileName, 0, info->FileNameLength / sizeof(wchar_t));
    u16string changedPath(changedPathW.begin(), changedPathW.end());
    if (!changedPath.empty()) {
        changedPath.insert(0, 1, u'\\');
        changedPath.insert(0, path);
    }
    // TODO Remove long prefix for path once?
    if (changedPath.length() >= 4 && changedPath.substr(0, 4) == u"\\\\?\\") {
        if (changedPath.length() >= 8 && changedPath.substr(0, 8) == u"\\\\?\\UNC\\") {
            changedPath.erase(0, 8).insert(0, u"\\\\");
        } else {
            changedPath.erase(0, 4);
        }
    }

    log_fine(server->getThreadEnv(), L"Change detected: 0x%x '%ls'", info->Action, changedPathW.c_str());

    jint type;
    if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
        type = FILE_EVENT_CREATED;
    } else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
        type = FILE_EVENT_REMOVED;
    } else if (info->Action == FILE_ACTION_MODIFIED) {
        type = FILE_EVENT_MODIFIED;
    } else {
        log_warning(server->getThreadEnv(), L"Unknown event 0x%x for %ls", info->Action, changedPathW.c_str());
        type = FILE_EVENT_UNKNOWN;
    }

    server->reportEvent(type, changedPath);
}

//
// Server
//

Server::Server(JNIEnv* env, jobject watcherCallback)
    : AbstractServer(env, watcherCallback) {
    startThread();
    // TODO Error handling
    SetThreadPriority(this->watcherThread.native_handle(), THREAD_PRIORITY_ABOVE_NORMAL);
}

Server::~Server() {
}

void Server::runLoop(JNIEnv* env, function<void()> notifyStarted) {
    log_info(env, L"Server thread %d with handle %p running", GetCurrentThreadId(), watcherThread.native_handle());

    notifyStarted();

    while (!terminate || watchPoints.size() > 0) {
        SleepEx(INFINITE, true);
    }

    log_info(env, L"Server thread %d finishing", GetCurrentThreadId());
}

void Server::startWatching(JNIEnv* env, const u16string& path) {
    wstring pathW(path.begin(), path.end());
    HANDLE directoryHandle = CreateFileW(
        pathW.c_str(),          // pointer to the file name
        FILE_LIST_DIRECTORY,    // access (read/write) mode
        CREATE_SHARE,           // share mode
        NULL,                   // security descriptor
        OPEN_EXISTING,          // how to create
        CREATE_FLAGS,           // file attributes
        NULL                    // file with attributes to copy
    );

    if (directoryHandle == INVALID_HANDLE_VALUE) {
        log_severe(env, L"Couldn't get file handle for '%ls': %d", pathW.c_str(), GetLastError());
        // TODO Error handling
        return;
    }

    WatchPoint* watchPoint = new WatchPoint(this, path, directoryHandle);

    HANDLE threadHandle = watcherThread.native_handle();
    int ret = watchPoint->awaitListeningStarted(threadHandle);
    switch (ret) {
        case WATCH_LISTENING:
            watchPoints.push_back(watchPoint);
            break;
        default:
            log_severe(env, L"Couldn't start listening to '%ls': %d", pathW.c_str(), ret);
            delete watchPoint;
            // TODO Error handling
            break;
    }
}

void Server::reportFinished(WatchPoint* watchPoint) {
    watchPoints.remove(watchPoint);
    delete watchPoint;
}

void Server::reportEvent(jint type, const u16string& changedPath) {
    JNIEnv* env = getThreadEnv();
    reportChange(env, type, changedPath);
}

static void CALLBACK requestTerminationCallback(_In_ ULONG_PTR arg) {
    Server* server = (Server*) arg;
    server->requestTermination();
}

void Server::requestTermination() {
    terminate = true;
    // Make copy so terminated entries can be removed
    list<WatchPoint*> copyWatchPoints(watchPoints);
    for (auto& watchPoint : copyWatchPoints) {
        watchPoint->close();
    }
}

void Server::close(JNIEnv* env) {
    HANDLE threadHandle = watcherThread.native_handle();
    log_fine(env, L"Requesting termination of server thread %p", threadHandle);
    int ret = QueueUserAPC(requestTerminationCallback, threadHandle, (ULONG_PTR) this);
    if (ret == 0) {
        log_severe(env, L"Couldn't send termination request to thread %p: %d", threadHandle, GetLastError());
    } else {
        watcherThread.join();
    }
}

bool isAbsoluteLocalPath(const u16string& path) {
    if (path.length() < 3) {
        return false;
    }
    return ((u'a' <= path[0] && path[0] <= u'z') || (u'A' <= path[0] && path[0] <= u'Z'))
        && path[1] == u':'
        && path[2] == u'\\';
}

bool isAbsoluteUncPath(const u16string& path) {
    if (path.length() < 3) {
        return false;
    }
    return path[0] == u'\\' && path[1] == u'\\';
}

void convertToLongPathIfNeeded(u16string& path) {
    // Technically, this should be MAX_PATH (i.e. 260), except some Win32 API related
    // to working with directory paths are actually limited to 240. It is just
    // safer/simpler to cover both cases in one code path.
    if (path.length() <= 240) {
        return;
    }

    if (isAbsoluteLocalPath(path)) {
        // Format: C:\... -> \\?\C:\...
        path.insert(0, u"\\\\?\\");
    } else if (isAbsoluteUncPath(path)) {
        // In this case, we need to skip the first 2 characters:
        // Format: \\server\share\... -> \\?\UNC\server\share\...
        path.erase(0, 2);
        path.insert(0, u"\\\\?\\UNC\\");
    } else {
        // It is some sort of unknown format, don't mess with it
    }
}

//
// JNI calls
//

JNIEXPORT jobject JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_startWatching(JNIEnv* env, jclass target, jobjectArray paths, jobject javaCallback, jobject result) {
    Server* server = new Server(env, javaCallback);

    int watchPointCount = env->GetArrayLength(paths);
    for (int i = 0; i < watchPointCount; i++) {
        jstring javaPath = (jstring) env->GetObjectArrayElement(paths, i);
        jsize javaPathLength = env->GetStringLength(javaPath);
        const jchar* javaPathChars = env->GetStringCritical(javaPath, nullptr);
        if (javaPathChars == NULL) {
            throw FileWatcherException("Could not get Java string character");
        }
        u16string pathStr((char16_t*) javaPathChars, javaPathLength);
        env->ReleaseStringCritical(javaPath, javaPathChars);
        convertToLongPathIfNeeded(pathStr);
        server->startWatching(env, pathStr);
    }

    jclass clsWatch = env->FindClass("net/rubygrapefruit/platform/internal/jni/WindowsFileEventFunctions$WatcherImpl");
    jmethodID constructor = env->GetMethodID(clsWatch, "<init>", "(Ljava/lang/Object;)V");
    return env->NewObject(clsWatch, constructor, env->NewDirectByteBuffer(server, sizeof(server)));
}

JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_stopWatching(JNIEnv* env, jclass target, jobject detailsObj, jobject result) {
    Server* server = (Server*) env->GetDirectBufferAddress(detailsObj);
    server->close(env);
    delete server;
}

#endif
<commit_msg>Add comment<commit_after>#ifdef _WIN32

#include "win_fsnotifier.h"

using namespace std;

//
// WatchPoint
//

WatchPoint::WatchPoint(Server* server, const u16string& path, HANDLE directoryHandle) {
    this->server = server;
    this->path = path;
    this->buffer = (FILE_NOTIFY_INFORMATION*) malloc(EVENT_BUFFER_SIZE);
    ZeroMemory(&this->overlapped, sizeof(OVERLAPPED));
    this->overlapped.hEvent = this;
    this->directoryHandle = directoryHandle;
    this->status = WATCH_UNINITIALIZED;
}

WatchPoint::~WatchPoint() {
    free(buffer);
}

void WatchPoint::close() {
    BOOL ret = CancelIo(directoryHandle);
    if (!ret) {
        log_severe(server->getThreadEnv(), L"Couldn't cancel I/O %p for '%ls': %d", directoryHandle, path.c_str(), GetLastError());
    }
    ret = CloseHandle(directoryHandle);
    if (!ret) {
        log_severe(server->getThreadEnv(), L"Couldn't close handle %p for '%ls': %d", directoryHandle, path.c_str(), GetLastError());
    }
}

static void CALLBACK startWatchCallback(_In_ ULONG_PTR arg) {
    WatchPoint* watchPoint = (WatchPoint*) arg;
    watchPoint->listen();
}

int WatchPoint::awaitListeningStarted(HANDLE threadHandle) {
    unique_lock<mutex> lock(listenerMutex);
    QueueUserAPC(startWatchCallback, threadHandle, (ULONG_PTR) this);
    listenerStarted.wait(lock);
    return status;
}

void WatchPoint::listen() {
    BOOL success = ReadDirectoryChangesW(
        directoryHandle,        // handle to directory
        buffer,                 // read results buffer
        EVENT_BUFFER_SIZE,      // length of buffer
        TRUE,                   // include children
        EVENT_MASK,             // filter conditions
        NULL,                   // bytes returned
        &overlapped,            // overlapped buffer
        &handleEventCallback    // completion routine
    );

    unique_lock<mutex> lock(listenerMutex);
    if (success) {
        status = WATCH_LISTENING;
    } else {
        status = WATCH_FAILED_TO_LISTEN;
        log_warning(server->getThreadEnv(), L"Couldn't start watching %p for '%ls', error = %d", directoryHandle, path.c_str(), GetLastError());
        // TODO Error handling
    }
    listenerStarted.notify_all();
}

static void CALLBACK handleEventCallback(DWORD errorCode, DWORD bytesTransferred, LPOVERLAPPED overlapped) {
    WatchPoint* watchPoint = (WatchPoint*) overlapped->hEvent;
    watchPoint->handleEvent(errorCode, bytesTransferred);
}

void WatchPoint::handleEvent(DWORD errorCode, DWORD bytesTransferred) {
    status = WATCH_NOT_LISTENING;

    if (errorCode == ERROR_OPERATION_ABORTED) {
        log_info(server->getThreadEnv(), L"Finished watching '%ls'", path.c_str());
        status = WATCH_FINISHED;
        server->reportFinished(this);
        return;
    }

    if (bytesTransferred == 0) {
        // don't send dirty too much, everything is changed anyway
        // TODO Understand what this does
        // if (WaitForSingleObject(stopEventHandle, 500) == WAIT_OBJECT_0)
        //    break;

        // Got a buffer overflow => current changes lost => send INVALIDATE on root
        server->reportEvent(FILE_EVENT_INVALIDATE, path);
    } else {
        FILE_NOTIFY_INFORMATION* current = buffer;
        for (;;) {
            handlePathChanged(current);
            if (current->NextEntryOffset == 0) {
                break;
            }
            current = (FILE_NOTIFY_INFORMATION*) (((BYTE*) current) + current->NextEntryOffset);
        }
    }

    listen();
    if (status != WATCH_LISTENING) {
        server->reportFinished(this);
    }
}

void WatchPoint::handlePathChanged(FILE_NOTIFY_INFORMATION* info) {
    wstring changedPathW = wstring(info->FileName, 0, info->FileNameLength / sizeof(wchar_t));
    u16string changedPath(changedPathW.begin(), changedPathW.end());
    // TODO Do we ever get an empty path?
    if (!changedPath.empty()) {
        changedPath.insert(0, 1, u'\\');
        changedPath.insert(0, path);
    }
    // TODO Remove long prefix for path once?
    if (changedPath.length() >= 4 && changedPath.substr(0, 4) == u"\\\\?\\") {
        if (changedPath.length() >= 8 && changedPath.substr(0, 8) == u"\\\\?\\UNC\\") {
            changedPath.erase(0, 8).insert(0, u"\\\\");
        } else {
            changedPath.erase(0, 4);
        }
    }

    log_fine(server->getThreadEnv(), L"Change detected: 0x%x '%ls'", info->Action, changedPathW.c_str());

    jint type;
    if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
        type = FILE_EVENT_CREATED;
    } else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
        type = FILE_EVENT_REMOVED;
    } else if (info->Action == FILE_ACTION_MODIFIED) {
        type = FILE_EVENT_MODIFIED;
    } else {
        log_warning(server->getThreadEnv(), L"Unknown event 0x%x for %ls", info->Action, changedPathW.c_str());
        type = FILE_EVENT_UNKNOWN;
    }

    server->reportEvent(type, changedPath);
}

//
// Server
//

Server::Server(JNIEnv* env, jobject watcherCallback)
    : AbstractServer(env, watcherCallback) {
    startThread();
    // TODO Error handling
    SetThreadPriority(this->watcherThread.native_handle(), THREAD_PRIORITY_ABOVE_NORMAL);
}

Server::~Server() {
}

void Server::runLoop(JNIEnv* env, function<void()> notifyStarted) {
    log_info(env, L"Server thread %d with handle %p running", GetCurrentThreadId(), watcherThread.native_handle());

    notifyStarted();

    while (!terminate || watchPoints.size() > 0) {
        SleepEx(INFINITE, true);
    }

    log_info(env, L"Server thread %d finishing", GetCurrentThreadId());
}

void Server::startWatching(JNIEnv* env, const u16string& path) {
    wstring pathW(path.begin(), path.end());
    HANDLE directoryHandle = CreateFileW(
        pathW.c_str(),          // pointer to the file name
        FILE_LIST_DIRECTORY,    // access (read/write) mode
        CREATE_SHARE,           // share mode
        NULL,                   // security descriptor
        OPEN_EXISTING,          // how to create
        CREATE_FLAGS,           // file attributes
        NULL                    // file with attributes to copy
    );

    if (directoryHandle == INVALID_HANDLE_VALUE) {
        log_severe(env, L"Couldn't get file handle for '%ls': %d", pathW.c_str(), GetLastError());
        // TODO Error handling
        return;
    }

    WatchPoint* watchPoint = new WatchPoint(this, path, directoryHandle);

    HANDLE threadHandle = watcherThread.native_handle();
    int ret = watchPoint->awaitListeningStarted(threadHandle);
    switch (ret) {
        case WATCH_LISTENING:
            watchPoints.push_back(watchPoint);
            break;
        default:
            log_severe(env, L"Couldn't start listening to '%ls': %d", pathW.c_str(), ret);
            delete watchPoint;
            // TODO Error handling
            break;
    }
}

void Server::reportFinished(WatchPoint* watchPoint) {
    watchPoints.remove(watchPoint);
    delete watchPoint;
}

void Server::reportEvent(jint type, const u16string& changedPath) {
    JNIEnv* env = getThreadEnv();
    reportChange(env, type, changedPath);
}

static void CALLBACK requestTerminationCallback(_In_ ULONG_PTR arg) {
    Server* server = (Server*) arg;
    server->requestTermination();
}

void Server::requestTermination() {
    terminate = true;
    // Make copy so terminated entries can be removed
    list<WatchPoint*> copyWatchPoints(watchPoints);
    for (auto& watchPoint : copyWatchPoints) {
        watchPoint->close();
    }
}

void Server::close(JNIEnv* env) {
    HANDLE threadHandle = watcherThread.native_handle();
    log_fine(env, L"Requesting termination of server thread %p", threadHandle);
    int ret = QueueUserAPC(requestTerminationCallback, threadHandle, (ULONG_PTR) this);
    if (ret == 0) {
        log_severe(env, L"Couldn't send termination request to thread %p: %d", threadHandle, GetLastError());
    } else {
        watcherThread.join();
    }
}

bool isAbsoluteLocalPath(const u16string& path) {
    if (path.length() < 3) {
        return false;
    }
    return ((u'a' <= path[0] && path[0] <= u'z') || (u'A' <= path[0] && path[0] <= u'Z'))
        && path[1] == u':'
        && path[2] == u'\\';
}

bool isAbsoluteUncPath(const u16string& path) {
    if (path.length() < 3) {
        return false;
    }
    return path[0] == u'\\' && path[1] == u'\\';
}

void convertToLongPathIfNeeded(u16string& path) {
    // Technically, this should be MAX_PATH (i.e. 260), except some Win32 API related
    // to working with directory paths are actually limited to 240. It is just
    // safer/simpler to cover both cases in one code path.
    if (path.length() <= 240) {
        return;
    }

    if (isAbsoluteLocalPath(path)) {
        // Format: C:\... -> \\?\C:\...
        path.insert(0, u"\\\\?\\");
    } else if (isAbsoluteUncPath(path)) {
        // In this case, we need to skip the first 2 characters:
        // Format: \\server\share\... -> \\?\UNC\server\share\...
        path.erase(0, 2);
        path.insert(0, u"\\\\?\\UNC\\");
    } else {
        // It is some sort of unknown format, don't mess with it
    }
}

//
// JNI calls
//

JNIEXPORT jobject JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_startWatching(JNIEnv* env, jclass target, jobjectArray paths, jobject javaCallback, jobject result) {
    Server* server = new Server(env, javaCallback);

    int watchPointCount = env->GetArrayLength(paths);
    for (int i = 0; i < watchPointCount; i++) {
        jstring javaPath = (jstring) env->GetObjectArrayElement(paths, i);
        jsize javaPathLength = env->GetStringLength(javaPath);
        const jchar* javaPathChars = env->GetStringCritical(javaPath, nullptr);
        if (javaPathChars == NULL) {
            throw FileWatcherException("Could not get Java string character");
        }
        u16string pathStr((char16_t*) javaPathChars, javaPathLength);
        env->ReleaseStringCritical(javaPath, javaPathChars);
        convertToLongPathIfNeeded(pathStr);
        server->startWatching(env, pathStr);
    }

    jclass clsWatch = env->FindClass("net/rubygrapefruit/platform/internal/jni/WindowsFileEventFunctions$WatcherImpl");
    jmethodID constructor = env->GetMethodID(clsWatch, "<init>", "(Ljava/lang/Object;)V");
    return env->NewObject(clsWatch, constructor, env->NewDirectByteBuffer(server, sizeof(server)));
}

JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_stopWatching(JNIEnv* env, jclass target, jobject detailsObj, jobject result) {
    Server* server = (Server*) env->GetDirectBufferAddress(detailsObj);
    server->close(env);
    delete server;
}

#endif
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Vassil Vassilev <vvasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "BackendPasses.h"

#include "llvm/Analysis/InlineCost.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/InlinerPass.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Scalar.h"

//#include "clang/Basic/LangOptions.h"
//#include "clang/Basic/TargetOptions.h"
#include "clang/Frontend/CodeGenOptions.h"

using namespace cling;
using namespace clang;
using namespace llvm;
using namespace llvm::legacy;

namespace {
  class KeepLocalFuncPass: public FunctionPass {
    static char ID;
  public:
    KeepLocalFuncPass() : FunctionPass(ID) {}
    bool runOnFunction(Function &F) override {
      if (F.isDeclaration())
        return false; // no change.

      // F is a definition.

      if (!F.isDiscardableIfUnused())
        return false;

      llvm::GlobalValue::LinkageTypes LT = F.getLinkage();
      if (LT == llvm::GlobalValue::InternalLinkage
          || LT == llvm::GlobalValue::PrivateLinkage) {
        F.setLinkage(llvm::GlobalValue::ExternalLinkage);
        return true; // a change!
      }
      return false;
    }
  };
}

char KeepLocalFuncPass::ID = 0;

BackendPasses::~BackendPasses() {
  //delete m_PMBuilder->Inliner;
}

void BackendPasses::CreatePasses(llvm::Module& M)
{
  // From BackEndUtil's clang::EmitAssemblyHelper::CreatePasses().

  CodeGenOptions& CGOpts_ = const_cast<CodeGenOptions&>(m_CGOpts);
  // DON'T: we will not find our symbols...
  //CGOpts_.CXXCtorDtorAliases = 1;

#if 0
  // Default clang -O2 on Linux 64bit also has the following, but see
  // CIFactory.cpp.
  CGOpts_.DisableFPElim = 0;
  CGOpts_.DiscardValueNames = 1;
  CGOpts_.OmitLeafFramePointer = 1;
  CGOpts_.OptimizationLevel = 2;
  CGOpts_.RelaxAll = 0;
  CGOpts_.UnrollLoops = 1;
  CGOpts_.VectorizeLoop = 1;
  CGOpts_.VectorizeSLP = 1;
#endif

  // Better inlining is pending https://bugs.llvm.org//show_bug.cgi?id=19668
  // and its consequence https://sft.its.cern.ch/jira/browse/ROOT-7111
  // shown e.g. by roottest/cling/stl/map/badstringMap
  CGOpts_.setInlining(CodeGenOptions::NormalInlining);

  unsigned OptLevel = m_CGOpts.OptimizationLevel;

  CodeGenOptions::InliningMethod Inlining = m_CGOpts.getInlining();

  // Handle disabling of LLVM optimization, where we want to preserve the
  // internal module before any optimization.
  if (m_CGOpts.DisableLLVMOpts) {
    OptLevel = 0;
    // Always keep at least ForceInline - NoInlining is deadly for libc++.
    // Inlining = CGOpts.NoInlining;
  }

  llvm::PassManagerBuilder PMBuilder;
  PMBuilder.OptLevel = OptLevel;
  PMBuilder.SizeLevel = m_CGOpts.OptimizeSize;
  PMBuilder.BBVectorize = m_CGOpts.VectorizeBB;
  PMBuilder.SLPVectorize = m_CGOpts.VectorizeSLP;
  PMBuilder.LoopVectorize = m_CGOpts.VectorizeLoop;

  PMBuilder.DisableTailCalls = m_CGOpts.DisableTailCalls;
  PMBuilder.DisableUnitAtATime = !m_CGOpts.UnitAtATime;
  PMBuilder.DisableUnrollLoops = !m_CGOpts.UnrollLoops;
  PMBuilder.MergeFunctions = m_CGOpts.MergeFunctions;
  PMBuilder.RerollLoops = m_CGOpts.RerollLoops;

  PMBuilder.LibraryInfo = new TargetLibraryInfoImpl(m_TM.getTargetTriple());

  switch (Inlining) {
  case CodeGenOptions::OnlyHintInlining: // fall-through:
    case CodeGenOptions::NoInlining: {
      assert(0 && "libc++ requires at least OnlyAlwaysInlining!");
      break;
    }
    case CodeGenOptions::NormalInlining: {
      PMBuilder.Inliner =
        createFunctionInliningPass(OptLevel, m_CGOpts.OptimizeSize);
      break;
    }
    case CodeGenOptions::OnlyAlwaysInlining:
      // Respect always_inline.
      if (OptLevel == 0)
        // Do not insert lifetime intrinsics at -O0.
        PMBuilder.Inliner = createAlwaysInlinerPass(false);
      else
        PMBuilder.Inliner = createAlwaysInlinerPass();
      break;
  }

  // Set up the per-module pass manager.
  m_MPM.reset(new legacy::PassManager());

  m_MPM->add(createTargetTransformInfoWrapperPass(m_TM.getTargetIRAnalysis()));

  // Add target-specific passes that need to run as early as possible.
  PMBuilder.addExtension(
                         PassManagerBuilder::EP_EarlyAsPossible,
                         [&](const PassManagerBuilder &,
                             legacy::PassManagerBase &PM) {
                           m_TM.addEarlyAsPossiblePasses(PM);
                         });

  PMBuilder.addExtension(PassManagerBuilder::EP_EarlyAsPossible,
                         [&](const PassManagerBuilder &,
                             legacy::PassManagerBase &PM) {
                              PM.add(createAddDiscriminatorsPass());
                            });

  //if (!CGOpts.RewriteMapFiles.empty())
  //  addSymbolRewriterPass(CGOpts, m_MPM);

  PMBuilder.populateModulePassManager(*m_MPM);

  m_FPM.reset(new legacy::FunctionPassManager(&M));
  m_FPM->add(new KeepLocalFuncPass());
  m_FPM->add(createTargetTransformInfoWrapperPass(m_TM.getTargetIRAnalysis()));
  if (m_CGOpts.VerifyModule)
      m_FPM->add(createVerifierPass());
  PMBuilder.populateFunctionPassManager(*m_FPM);
}

void BackendPasses::runOnModule(Module& M) {

  if (!m_MPM)
    CreatePasses(M);
  // Set up the per-function pass manager.

  // Run the per-function passes on the module.
  m_FPM->doInitialization();
  for (auto&& I: M.functions())
    if (!I.isDeclaration())
      m_FPM->run(I);
  m_FPM->doFinalization();

  m_MPM->run(M);
}
<commit_msg>Also handle GlobalVariables. Fixes Recursion/RecursiveClingInstances.C.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// author:  Vassil Vassilev <vvasilev@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

#include "BackendPasses.h"

#include "llvm/Analysis/InlineCost.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/IPO.h"
#include "llvm/Transforms/IPO/InlinerPass.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
#include "llvm/Transforms/Scalar.h"

//#include "clang/Basic/LangOptions.h"
//#include "clang/Basic/TargetOptions.h"
#include "clang/Frontend/CodeGenOptions.h"

using namespace cling;
using namespace clang;
using namespace llvm;
using namespace llvm::legacy;

namespace {
  class KeepLocalGVPass: public ModulePass {
    static char ID;

    bool runOnGlobal(GlobalValue& GV) {
      if (GV.isDeclaration())
        return false; // no change.

      // GV is a definition.

      llvm::GlobalValue::LinkageTypes LT = GV.getLinkage();
      if (!GV.isDiscardableIfUnused(LT))
        return false;

      if (LT == llvm::GlobalValue::InternalLinkage
          || LT == llvm::GlobalValue::PrivateLinkage) {
        GV.setLinkage(llvm::GlobalValue::ExternalLinkage);
        return true; // a change!
      }
      return false;
    }

  public:
    KeepLocalGVPass() : ModulePass(ID) {}

    bool runOnModule(Module &M) override {
      bool ret = false;
      for (auto &&F: M)
        ret |= runOnGlobal(F);
      for (auto &&G: M.globals())
        ret |= runOnGlobal(G);
      return ret;
    }
  };
}

char KeepLocalGVPass::ID = 0;

BackendPasses::~BackendPasses() {
  //delete m_PMBuilder->Inliner;
}

void BackendPasses::CreatePasses(llvm::Module& M)
{
  // From BackEndUtil's clang::EmitAssemblyHelper::CreatePasses().

  CodeGenOptions& CGOpts_ = const_cast<CodeGenOptions&>(m_CGOpts);
  // DON'T: we will not find our symbols...
  //CGOpts_.CXXCtorDtorAliases = 1;

#if 0
  // Default clang -O2 on Linux 64bit also has the following, but see
  // CIFactory.cpp.
  CGOpts_.DisableFPElim = 0;
  CGOpts_.DiscardValueNames = 1;
  CGOpts_.OmitLeafFramePointer = 1;
  CGOpts_.OptimizationLevel = 2;
  CGOpts_.RelaxAll = 0;
  CGOpts_.UnrollLoops = 1;
  CGOpts_.VectorizeLoop = 1;
  CGOpts_.VectorizeSLP = 1;
#endif

  // Better inlining is pending https://bugs.llvm.org//show_bug.cgi?id=19668
  // and its consequence https://sft.its.cern.ch/jira/browse/ROOT-7111
  // shown e.g. by roottest/cling/stl/map/badstringMap
  CGOpts_.setInlining(CodeGenOptions::NormalInlining);

  unsigned OptLevel = m_CGOpts.OptimizationLevel;

  CodeGenOptions::InliningMethod Inlining = m_CGOpts.getInlining();

  // Handle disabling of LLVM optimization, where we want to preserve the
  // internal module before any optimization.
  if (m_CGOpts.DisableLLVMOpts) {
    OptLevel = 0;
    // Always keep at least ForceInline - NoInlining is deadly for libc++.
    // Inlining = CGOpts.NoInlining;
  }

  llvm::PassManagerBuilder PMBuilder;
  PMBuilder.OptLevel = OptLevel;
  PMBuilder.SizeLevel = m_CGOpts.OptimizeSize;
  PMBuilder.BBVectorize = m_CGOpts.VectorizeBB;
  PMBuilder.SLPVectorize = m_CGOpts.VectorizeSLP;
  PMBuilder.LoopVectorize = m_CGOpts.VectorizeLoop;

  PMBuilder.DisableTailCalls = m_CGOpts.DisableTailCalls;
  PMBuilder.DisableUnitAtATime = !m_CGOpts.UnitAtATime;
  PMBuilder.DisableUnrollLoops = !m_CGOpts.UnrollLoops;
  PMBuilder.MergeFunctions = m_CGOpts.MergeFunctions;
  PMBuilder.RerollLoops = m_CGOpts.RerollLoops;

  PMBuilder.LibraryInfo = new TargetLibraryInfoImpl(m_TM.getTargetTriple());

  switch (Inlining) {
  case CodeGenOptions::OnlyHintInlining: // fall-through:
    case CodeGenOptions::NoInlining: {
      assert(0 && "libc++ requires at least OnlyAlwaysInlining!");
      break;
    }
    case CodeGenOptions::NormalInlining: {
      PMBuilder.Inliner =
        createFunctionInliningPass(OptLevel, m_CGOpts.OptimizeSize);
      break;
    }
    case CodeGenOptions::OnlyAlwaysInlining:
      // Respect always_inline.
      if (OptLevel == 0)
        // Do not insert lifetime intrinsics at -O0.
        PMBuilder.Inliner = createAlwaysInlinerPass(false);
      else
        PMBuilder.Inliner = createAlwaysInlinerPass();
      break;
  }

  // Set up the per-module pass manager.
  m_MPM.reset(new legacy::PassManager());

  m_MPM->add(new KeepLocalGVPass());
  m_MPM->add(createTargetTransformInfoWrapperPass(m_TM.getTargetIRAnalysis()));

  // Add target-specific passes that need to run as early as possible.
  PMBuilder.addExtension(
                         PassManagerBuilder::EP_EarlyAsPossible,
                         [&](const PassManagerBuilder &,
                             legacy::PassManagerBase &PM) {
                           m_TM.addEarlyAsPossiblePasses(PM);
                         });

  PMBuilder.addExtension(PassManagerBuilder::EP_EarlyAsPossible,
                         [&](const PassManagerBuilder &,
                             legacy::PassManagerBase &PM) {
                              PM.add(createAddDiscriminatorsPass());
                            });

  //if (!CGOpts.RewriteMapFiles.empty())
  //  addSymbolRewriterPass(CGOpts, m_MPM);

  PMBuilder.populateModulePassManager(*m_MPM);

  m_FPM.reset(new legacy::FunctionPassManager(&M));
  m_FPM->add(createTargetTransformInfoWrapperPass(m_TM.getTargetIRAnalysis()));
  if (m_CGOpts.VerifyModule)
      m_FPM->add(createVerifierPass());
  PMBuilder.populateFunctionPassManager(*m_FPM);
}

void BackendPasses::runOnModule(Module& M) {

  if (!m_MPM)
    CreatePasses(M);
  // Set up the per-function pass manager.

  // Run the per-function passes on the module.
  m_FPM->doInitialization();
  for (auto&& I: M.functions())
    if (!I.isDeclaration())
      m_FPM->run(I);
  m_FPM->doFinalization();

  m_MPM->run(M);
}
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author:  Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------

#include "DeclCollector.h"

#include "cling/Interpreter/Transaction.h"

#include "clang/AST/Decl.h"
#include "clang/AST/DeclGroup.h"

using namespace clang;

namespace cling {

  // pin the vtable here.
  DeclCollector::~DeclCollector() {
  }

  bool DeclCollector::HandleTopLevelDecl(DeclGroupRef DGR) {
    m_CurTransaction->appendUnique(DGR);
    return true;
  }

  void DeclCollector::HandleInterestingDecl(DeclGroupRef DGR) {
    assert("Not implemented yet!");
  }

  // Does more than we want:
  // if there is class A {enum E {kEnum = 1};};
  // we get two different tag decls one for A and one for E. This is not that
  // bad because esentially it has no effect on codegen but it differs from what
  // one'd expect. For now rely on the HandleTopLevelDecl to provide all the
  // declarations in the transaction.
  void DeclCollector::HandleTagDeclDefinition(TagDecl* TD) {
    // Intentional no-op.
  }

  void DeclCollector::HandleVTable(CXXRecordDecl* RD, bool DefinitionRequired) {
    assert(0 && "Not implemented yet!");
  }

  void DeclCollector::CompleteTentativeDefinition(VarDecl* VD) {
    assert(0 && "Not implemented yet!");
  }

  void DeclCollector::HandleTranslationUnit(ASTContext& Ctx) {
  }
} // namespace cling
<commit_msg>HandleVTable assert is far too intrusive to stay.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
// version: $Id$
// author:  Vassil Vassilev <vasil.georgiev.vasilev@cern.ch>
//------------------------------------------------------------------------------

#include "DeclCollector.h"

#include "cling/Interpreter/Transaction.h"

#include "clang/AST/Decl.h"
#include "clang/AST/DeclGroup.h"

using namespace clang;

namespace cling {

  // pin the vtable here.
  DeclCollector::~DeclCollector() {
  }

  bool DeclCollector::HandleTopLevelDecl(DeclGroupRef DGR) {
    m_CurTransaction->appendUnique(DGR);
    return true;
  }

  void DeclCollector::HandleInterestingDecl(DeclGroupRef DGR) {
    assert(0 && "Not implemented yet!");
  }

  // Does more than we want:
  // if there is class A {enum E {kEnum = 1};};
  // we get two different tag decls one for A and one for E. This is not that
  // bad because esentially it has no effect on codegen but it differs from what
  // one'd expect. For now rely on the HandleTopLevelDecl to provide all the
  // declarations in the transaction.
  void DeclCollector::HandleTagDeclDefinition(TagDecl* TD) {
    // Intentional no-op.
  }

  void DeclCollector::HandleVTable(CXXRecordDecl* RD, bool DefinitionRequired) {
    // Intentional no-op. It comes through Sema::DefineUsedVTables, which
    // comes either Sema::ActOnEndOfTranslationUnit or while instantiating a
    // template. In our case we will do it on transaction commit, without
    // keeping track of used vtables, because we have cases where we bypass the
    // clang/AST and directly ask the module so that we have to generate
    // everything without extra smartness.
  }

  void DeclCollector::CompleteTentativeDefinition(VarDecl* VD) {
    assert(0 && "Not implemented yet!");
  }

  void DeclCollector::HandleTranslationUnit(ASTContext& Ctx) {
  }
} // namespace cling
<|endoftext|>


<commit_before>#ifndef SIMPLESPMV_H
#define SIMPLESPMV_H

#include <sstream>

#include <Spark/Spmv.hpp>
#include <Spark/Utils.hpp>

namespace spark {
  namespace spmv {

    using EigenSparseMatrix = Eigen::SparseMatrix<double, Eigen::RowMajor, int32_t>;

    // pack values and colptr to reduce number of streams
#pragma pack(1)
    struct indptr_value {
      double value;
      int indptr;
      indptr_value(double _value, int _indptr) : value(_value), indptr(_indptr) {}
      indptr_value() : value(0), indptr(0) {}
    } __attribute__((packed));
#pragma pack()

    struct Partition {
      int nBlocks, n, paddingCycles, totalCycles, vector_load_cycles, outSize;
      int reductionCycles, emptyCycles;
      int m_colptr_unpaddedLength;
      int m_indptr_values_unpaddedLength;
      std::vector<int> m_colptr;
      std::vector<indptr_value> m_indptr_values;

      std::string to_string() {
        std::stringstream s;
        s << "Vector load cycles " << vector_load_cycles << std::endl;
        s << "Padding cycles = " << paddingCycles << std::endl;
        s << "Total cycles = " << totalCycles << std::endl;
        s << "Nrows = " << n << std::endl;
        s << "Partitions = " << nBlocks << std::endl;
        s << "Reduction cycles = " << reductionCycles << std::endl;
        std::cout << "Empty cycles = " << emptyCycles << std::endl;
        return s.str();
      }
    };

    // A parameterised, generic architecture for SpMV. Supported parameters are:
    // - input width
    // - number of pipes
    // - cache size per pipe
    // - TODO data type
    class SimpleSpmvArchitecture : public SpmvArchitecture {
      // architecture specific properties
      protected:
      int cacheSize, inputWidth, numPipes;
      EigenSparseMatrix mat;
      std::vector<Partition> partitions;

      virtual int countComputeCycles(uint32_t* v, int size, int inputWidth);

      public:

        SimpleSpmvArchitecture() :
          cacheSize(getPartitionSize()),
          inputWidth(getInputWidth()),
          numPipes(getNumPipes()) {}

        SimpleSpmvArchitecture(int _cacheSize, int  _inputWidth, int _numPipes) :
          cacheSize(_cacheSize),
          inputWidth(_inputWidth),
          numPipes(_numPipes) {}

        virtual ~SimpleSpmvArchitecture() {}

        virtual double getEstimatedClockCycles() {
          auto res = std::max_element(partitions.begin(), partitions.end(),
              [](const Partition& a, const Partition& b) {
                return a.totalCycles < b.totalCycles;
              });
          return res->totalCycles;
        }

        virtual double getGFlopsCount() {
          return 2 * this->mat.nonZeros() / 1E9;
        }

        // NOTE: only call this after a call to preprocessMatrix
        virtual ImplementationParameters getImplementationParameters() {
          // XXX bram usage for altera in double precision only (512 deep, 40 bits wide, so need 2 BRAMs)
          //int brams = (double)cacheSize * (double)inputWidth / 512.0 * 2.0;

          // XXX these should be architecture params
          int maxRows = 200000;
          const int virtex6EntriesPerBram = 512;

          LogicResourceUsage interPartitionReductionKernel(2768,1505, maxRows / virtex6EntriesPerBram, 0);
          LogicResourceUsage paddingKernel{400, 500, 0, 0};
          LogicResourceUsage spmvKernelPerInput{1466, 2060, cacheSize / virtex6EntriesPerBram, 10}; // includes cache
          LogicResourceUsage sm{800, 500, 0, 0};

          LogicResourceUsage spmvPerPipe =
            interPartitionReductionKernel +
            paddingKernel +
            spmvKernelPerInput * inputWidth +
            sm;

          LogicResourceUsage memoryPerPipe{3922, 8393, 160, 0};
          LogicResourceUsage memory{24000, 32000, 0, 0};

          //LogicResourceUsage designOther{};
          LogicResourceUsage designUsage = (spmvPerPipe + memoryPerPipe) * numPipes + memory;

          double memoryBandwidth =(double)inputWidth * numPipes * getFrequency() * 12.0 / 1E9;
          ImplementationParameters ip{designUsage, memoryBandwidth};
          //ip.clockFrequency = getFrequency() / 1E6; // MHz
          return ip;
        }

        virtual std::string to_string() {
          std::stringstream s;
          s << get_name();
          s << " " << cacheSize;
          s << " " << inputWidth;
          s << " " << numPipes;
          s << " " << getEstimatedClockCycles();
          s << " " << getEstimatedGFlops();
          return s.str();
        }

        virtual void preprocess(const EigenSparseMatrix& mat) override;

        virtual Eigen::VectorXd dfespmv(Eigen::VectorXd x) override;

        virtual std::string get_name() override {
          return std::string("Simple");
        }

      private:
        std::vector<EigenSparseMatrix> do_partition(
            const EigenSparseMatrix& mat,
            int numPipes);

        Partition do_blocking(
            const Eigen::SparseMatrix<double, Eigen::RowMajor, int32_t>& mat,
            int blockSize,
            int inputWidth);
    };

    template<typename T>
    class SimpleSpmvArchitectureSpace : public SpmvArchitectureSpace {
      // NOTE any raw pointers returned through the API of this class
      // are assumed to be wrapped in smart pointers by the base class
      spark::utils::Range cacheSizeR{1024, 4096, 512};
      spark::utils::Range inputWidthR{8, 100, 8};
      spark::utils::Range numPipesR{1, 6, 1};

      bool last = false;

      public:

      SimpleSpmvArchitectureSpace(
          spark::utils::Range numPipesRange,
          spark::utils::Range inputWidthRange,
          spark::utils::Range cacheSizeRange) {
        cacheSizeR = cacheSizeRange;
        inputWidthR = inputWidthRange;
        numPipesR = numPipesRange;
      }

      SimpleSpmvArchitectureSpace() {
      }

      protected:
      void restart() override {
        cacheSizeR.restart();
        inputWidthR.restart();
        numPipesR.restart();
        last = false;
      }

      T* doNext(){
        if (last)
          return nullptr;

        T* result = new T(cacheSizeR.crt, inputWidthR.crt, numPipesR.crt);

        ++cacheSizeR;
        if (cacheSizeR.at_start()) {
          ++inputWidthR;
          if (inputWidthR.at_start()) {
            ++numPipesR;
            if (numPipesR.at_start())
              last = true;
          }
        }

        return result;
      }
    };

    // FST based architecture, for now we assume it's the same though it probably isn't
    class FstSpmvArchitecture : public SimpleSpmvArchitecture {
      protected:

      virtual int countComputeCycles(uint32_t* v, int size, int inputWidth) override {
        int cycles = 0;
        for (int i = 0; i < size; i++) {
          int toread = v[i] - (i > 0 ? v[i - 1] : 0);
          do {
            toread -= std::min(toread, inputWidth);
            cycles++;
          } while (toread > 0);
        }
        return cycles;
      }

      public:
      FstSpmvArchitecture() : SimpleSpmvArchitecture() {}

      FstSpmvArchitecture(int _cacheSize, int  _inputWidth, int _numPipes) :
        SimpleSpmvArchitecture(_cacheSize, _inputWidth, _numPipes) {}

      virtual std::string get_name() override {
        return std::string("Fst");
      }

    };

    // Model for an architecture which can skip sequences of empty rows
    class SkipEmptyRowsArchitecture : public SimpleSpmvArchitecture {
      protected:

        std::vector<uint32_t> encodeEmptyRows(std::vector<uint32_t> pin, bool encode) {
          std::vector<uint32_t> encoded;
          if (!encode) {
            return pin;
          }

          int emptyRunLength = 0;
          for (size_t i = 0; i < pin.size(); i++) {
            uint32_t rowLength = pin[i] - (i == 0 ? 0 : pin[i - 1]);
            if (rowLength == 0) {
              emptyRunLength++;
            } else {
              if (emptyRunLength != 0) {
                encoded.push_back(emptyRunLength | (1 << 31));
              }
              emptyRunLength = 0;
              encoded.push_back(pin[i]);
            }
          }

          if (emptyRunLength != 0) {
            encoded.push_back(emptyRunLength | (1 << 31));
          }

          return encoded;
        }

        virtual spark::sparse::CsrMatrix preprocessBlock(
            const spark::sparse::CsrMatrix& in,
            int blockNumber,
            int nBlocks) override {
          bool encode = blockNumber != 0 && blockNumber != nBlocks - 1;
          return std::make_tuple(
              encodeEmptyRows(std::get<0>(in), encode),
              std::get<1>(in),
              std::get<2>(in));
        }

      public:
      SkipEmptyRowsArchitecture() : SimpleSpmvArchitecture(){}

      SkipEmptyRowsArchitecture(int _cacheSize, int  _inputWidth, int _numPipes) :
        SimpleSpmvArchitecture(_cacheSize, _inputWidth, _numPipes) {}

      virtual std::string get_name() override {
        return std::string("SkipEmpty");
      }
    };

    class PrefetchingArchitecture : public SkipEmptyRowsArchitecture {
      protected:

      public:
      PrefetchingArchitecture() : SkipEmptyRowsArchitecture(){}

      PrefetchingArchitecture(int _cacheSize, int  _inputWidth, int _numPipes) :
        SkipEmptyRowsArchitecture(_cacheSize, _inputWidth, _numPipes) {}

      virtual std::string get_name() override {
        return std::string("PrefetchingArchitecture");
      }
    };

  }
}

#endif /* end of include guard: SIMPLESPMV_H */
<commit_msg>Account for max rows based on matrix size<commit_after>#ifndef SIMPLESPMV_H
#define SIMPLESPMV_H

#include <sstream>

#include <Spark/Spmv.hpp>
#include <Spark/Utils.hpp>

namespace spark {
  namespace spmv {

    using EigenSparseMatrix = Eigen::SparseMatrix<double, Eigen::RowMajor, int32_t>;

    // pack values and colptr to reduce number of streams
#pragma pack(1)
    struct indptr_value {
      double value;
      int indptr;
      indptr_value(double _value, int _indptr) : value(_value), indptr(_indptr) {}
      indptr_value() : value(0), indptr(0) {}
    } __attribute__((packed));
#pragma pack()

    struct Partition {
      int nBlocks, n, paddingCycles, totalCycles, vector_load_cycles, outSize;
      int reductionCycles, emptyCycles;
      int m_colptr_unpaddedLength;
      int m_indptr_values_unpaddedLength;
      std::vector<int> m_colptr;
      std::vector<indptr_value> m_indptr_values;

      std::string to_string() {
        std::stringstream s;
        s << "Vector load cycles " << vector_load_cycles << std::endl;
        s << "Padding cycles = " << paddingCycles << std::endl;
        s << "Total cycles = " << totalCycles << std::endl;
        s << "Nrows = " << n << std::endl;
        s << "Partitions = " << nBlocks << std::endl;
        s << "Reduction cycles = " << reductionCycles << std::endl;
        std::cout << "Empty cycles = " << emptyCycles << std::endl;
        return s.str();
      }
    };

    // A parameterised, generic architecture for SpMV. Supported parameters are:
    // - input width
    // - number of pipes
    // - cache size per pipe
    // - TODO data type
    class SimpleSpmvArchitecture : public SpmvArchitecture {
      // architecture specific properties
      protected:
      int cacheSize, inputWidth, numPipes;
      EigenSparseMatrix mat;
      std::vector<Partition> partitions;

      virtual int countComputeCycles(uint32_t* v, int size, int inputWidth);

      public:

        SimpleSpmvArchitecture() :
          cacheSize(getPartitionSize()),
          inputWidth(getInputWidth()),
          numPipes(getNumPipes()) {}

        SimpleSpmvArchitecture(int _cacheSize, int  _inputWidth, int _numPipes) :
          cacheSize(_cacheSize),
          inputWidth(_inputWidth),
          numPipes(_numPipes) {}

        virtual ~SimpleSpmvArchitecture() {}

        virtual double getEstimatedClockCycles() {
          auto res = std::max_element(partitions.begin(), partitions.end(),
              [](const Partition& a, const Partition& b) {
                return a.totalCycles < b.totalCycles;
              });
          return res->totalCycles;
        }

        virtual double getGFlopsCount() {
          return 2 * this->mat.nonZeros() / 1E9;
        }

        // NOTE: only call this after a call to preprocessMatrix
        virtual ImplementationParameters getImplementationParameters() {
          // XXX bram usage for altera in double precision only (512 deep, 40 bits wide, so need 2 BRAMs)
          //int brams = (double)cacheSize * (double)inputWidth / 512.0 * 2.0;

          // XXX these should be architecture params
          int maxRows = (this->mat.rows() / 512) * 512;
          const int virtex6EntriesPerBram = 512;

          LogicResourceUsage interPartitionReductionKernel(2768,1505, maxRows / virtex6EntriesPerBram, 0);
          LogicResourceUsage paddingKernel{400, 500, 0, 0};
          LogicResourceUsage spmvKernelPerInput{1466, 2060, cacheSize / virtex6EntriesPerBram, 10}; // includes cache
          LogicResourceUsage sm{800, 500, 0, 0};

          LogicResourceUsage spmvPerPipe =
            interPartitionReductionKernel +
            paddingKernel +
            spmvKernelPerInput * inputWidth +
            sm;

          LogicResourceUsage memoryPerPipe{3922, 8393, 160, 0};
          LogicResourceUsage memory{24000, 32000, 0, 0};

          //LogicResourceUsage designOther{};
          LogicResourceUsage designUsage = (spmvPerPipe + memoryPerPipe) * numPipes + memory;

          double memoryBandwidth =(double)inputWidth * numPipes * getFrequency() * 12.0 / 1E9;
          ImplementationParameters ip{designUsage, memoryBandwidth};
          //ip.clockFrequency = getFrequency() / 1E6; // MHz
          return ip;
        }

        virtual std::string to_string() {
          std::stringstream s;
          s << get_name();
          s << " " << cacheSize;
          s << " " << inputWidth;
          s << " " << numPipes;
          s << " " << getEstimatedClockCycles();
          s << " " << getEstimatedGFlops();
          return s.str();
        }

        virtual void preprocess(const EigenSparseMatrix& mat) override;

        virtual Eigen::VectorXd dfespmv(Eigen::VectorXd x) override;

        virtual std::string get_name() override {
          return std::string("Simple");
        }

      private:
        std::vector<EigenSparseMatrix> do_partition(
            const EigenSparseMatrix& mat,
            int numPipes);

        Partition do_blocking(
            const Eigen::SparseMatrix<double, Eigen::RowMajor, int32_t>& mat,
            int blockSize,
            int inputWidth);
    };

    template<typename T>
    class SimpleSpmvArchitectureSpace : public SpmvArchitectureSpace {
      // NOTE any raw pointers returned through the API of this class
      // are assumed to be wrapped in smart pointers by the base class
      spark::utils::Range cacheSizeR{1024, 4096, 512};
      spark::utils::Range inputWidthR{8, 100, 8};
      spark::utils::Range numPipesR{1, 6, 1};

      bool last = false;

      public:

      SimpleSpmvArchitectureSpace(
          spark::utils::Range numPipesRange,
          spark::utils::Range inputWidthRange,
          spark::utils::Range cacheSizeRange) {
        cacheSizeR = cacheSizeRange;
        inputWidthR = inputWidthRange;
        numPipesR = numPipesRange;
      }

      SimpleSpmvArchitectureSpace() {
      }

      protected:
      void restart() override {
        cacheSizeR.restart();
        inputWidthR.restart();
        numPipesR.restart();
        last = false;
      }

      T* doNext(){
        if (last)
          return nullptr;

        T* result = new T(cacheSizeR.crt, inputWidthR.crt, numPipesR.crt);

        ++cacheSizeR;
        if (cacheSizeR.at_start()) {
          ++inputWidthR;
          if (inputWidthR.at_start()) {
            ++numPipesR;
            if (numPipesR.at_start())
              last = true;
          }
        }

        return result;
      }
    };

    // FST based architecture, for now we assume it's the same though it probably isn't
    class FstSpmvArchitecture : public SimpleSpmvArchitecture {
      protected:

      virtual int countComputeCycles(uint32_t* v, int size, int inputWidth) override {
        int cycles = 0;
        for (int i = 0; i < size; i++) {
          int toread = v[i] - (i > 0 ? v[i - 1] : 0);
          do {
            toread -= std::min(toread, inputWidth);
            cycles++;
          } while (toread > 0);
        }
        return cycles;
      }

      public:
      FstSpmvArchitecture() : SimpleSpmvArchitecture() {}

      FstSpmvArchitecture(int _cacheSize, int  _inputWidth, int _numPipes) :
        SimpleSpmvArchitecture(_cacheSize, _inputWidth, _numPipes) {}

      virtual std::string get_name() override {
        return std::string("Fst");
      }

    };

    // Model for an architecture which can skip sequences of empty rows
    class SkipEmptyRowsArchitecture : public SimpleSpmvArchitecture {
      protected:

        std::vector<uint32_t> encodeEmptyRows(std::vector<uint32_t> pin, bool encode) {
          std::vector<uint32_t> encoded;
          if (!encode) {
            return pin;
          }

          int emptyRunLength = 0;
          for (size_t i = 0; i < pin.size(); i++) {
            uint32_t rowLength = pin[i] - (i == 0 ? 0 : pin[i - 1]);
            if (rowLength == 0) {
              emptyRunLength++;
            } else {
              if (emptyRunLength != 0) {
                encoded.push_back(emptyRunLength | (1 << 31));
              }
              emptyRunLength = 0;
              encoded.push_back(pin[i]);
            }
          }

          if (emptyRunLength != 0) {
            encoded.push_back(emptyRunLength | (1 << 31));
          }

          return encoded;
        }

        virtual spark::sparse::CsrMatrix preprocessBlock(
            const spark::sparse::CsrMatrix& in,
            int blockNumber,
            int nBlocks) override {
          bool encode = blockNumber != 0 && blockNumber != nBlocks - 1;
          return std::make_tuple(
              encodeEmptyRows(std::get<0>(in), encode),
              std::get<1>(in),
              std::get<2>(in));
        }

      public:
      SkipEmptyRowsArchitecture() : SimpleSpmvArchitecture(){}

      SkipEmptyRowsArchitecture(int _cacheSize, int  _inputWidth, int _numPipes) :
        SimpleSpmvArchitecture(_cacheSize, _inputWidth, _numPipes) {}

      virtual std::string get_name() override {
        return std::string("SkipEmpty");
      }
    };

    class PrefetchingArchitecture : public SkipEmptyRowsArchitecture {
      protected:

      public:
      PrefetchingArchitecture() : SkipEmptyRowsArchitecture(){}

      PrefetchingArchitecture(int _cacheSize, int  _inputWidth, int _numPipes) :
        SkipEmptyRowsArchitecture(_cacheSize, _inputWidth, _numPipes) {}

      virtual std::string get_name() override {
        return std::string("PrefetchingArchitecture");
      }
    };

  }
}

#endif /* end of include guard: SIMPLESPMV_H */
<|endoftext|>