- moved a few things around to have them into better fitting places.
This commit is contained in:
parent
f6d6f310a9
commit
5a81a4ca16
20 changed files with 23 additions and 28 deletions
10831
src/scripting/backend/codegen.cpp
Normal file
10831
src/scripting/backend/codegen.cpp
Normal file
File diff suppressed because it is too large
Load diff
2136
src/scripting/backend/codegen.h
Normal file
2136
src/scripting/backend/codegen.h
Normal file
File diff suppressed because it is too large
Load diff
782
src/scripting/backend/dynarrays.cpp
Normal file
782
src/scripting/backend/dynarrays.cpp
Normal file
|
|
@ -0,0 +1,782 @@
|
|||
/*
|
||||
** dynarray.cpp
|
||||
**
|
||||
** internal data types for dynamic arrays
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2016-2017 Christoph Oelckers
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. 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.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
|
||||
** covered by the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation; either version 2 of the License, or (at
|
||||
** your option) any later version.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "tarray.h"
|
||||
#include "dobject.h"
|
||||
#include "thingdef.h"
|
||||
// We need one specific type for each of the 7 integral VM types and instantiate the needed functions for each of them.
|
||||
// Dynamic arrays cannot hold structs because for every type there'd need to be an internal implementation which is impossible.
|
||||
|
||||
typedef TArray<uint8_t> FDynArray_I8;
|
||||
typedef TArray<uint16_t> FDynArray_I16;
|
||||
typedef TArray<uint32_t> FDynArray_I32;
|
||||
typedef TArray<float> FDynArray_F32;
|
||||
typedef TArray<double> FDynArray_F64;
|
||||
typedef TArray<void*> FDynArray_Ptr;
|
||||
typedef TArray<FString> FDynArray_String;
|
||||
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
// Int8 array
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I8, Copy)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
|
||||
PARAM_POINTER(other, FDynArray_I8);
|
||||
*self = *other;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I8, Move)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
|
||||
PARAM_POINTER(other, FDynArray_I8);
|
||||
*self = std::move(*other);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I8, Find)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
|
||||
PARAM_INT(val);
|
||||
ACTION_RETURN_INT(self->Find(val));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I8, Push)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
|
||||
PARAM_INT(val);
|
||||
ACTION_RETURN_INT(self->Push(val));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I8, Pop)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
|
||||
ACTION_RETURN_BOOL(self->Pop());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I8, Delete)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
|
||||
PARAM_INT(index);
|
||||
PARAM_INT(count);
|
||||
self->Delete(index, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I8, Insert)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
|
||||
PARAM_INT(index);
|
||||
PARAM_INT(val);
|
||||
self->Insert(index, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I8, ShrinkToFit)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
|
||||
self->ShrinkToFit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I8, Grow)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
|
||||
PARAM_INT(count);
|
||||
self->Grow(count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I8, Resize)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
|
||||
PARAM_INT(count);
|
||||
self->Resize(count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I8, Reserve)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
|
||||
PARAM_INT(count);
|
||||
ACTION_RETURN_INT(self->Reserve(count));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I8, Max)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
|
||||
ACTION_RETURN_INT(self->Max());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I8, Clear)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I8);
|
||||
self->Clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
// Int16 array
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I16, Copy)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
|
||||
PARAM_POINTER(other, FDynArray_I16);
|
||||
*self = *other;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I16, Move)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
|
||||
PARAM_POINTER(other, FDynArray_I16);
|
||||
*self = std::move(*other);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I16, Find)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
|
||||
PARAM_INT(val);
|
||||
ACTION_RETURN_INT(self->Find(val));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I16, Push)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
|
||||
PARAM_INT(val);
|
||||
ACTION_RETURN_INT(self->Push(val));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I16, Pop)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
|
||||
ACTION_RETURN_BOOL(self->Pop());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I16, Delete)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
|
||||
PARAM_INT(index);
|
||||
PARAM_INT(count);
|
||||
self->Delete(index, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I16, Insert)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
|
||||
PARAM_INT(index);
|
||||
PARAM_INT(val);
|
||||
self->Insert(index, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I16, ShrinkToFit)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
|
||||
self->ShrinkToFit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I16, Grow)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
|
||||
PARAM_INT(count);
|
||||
self->Grow(count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I16, Resize)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
|
||||
PARAM_INT(count);
|
||||
self->Resize(count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I16, Reserve)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
|
||||
PARAM_INT(count);
|
||||
ACTION_RETURN_INT(self->Reserve(count));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I16, Max)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
|
||||
ACTION_RETURN_INT(self->Max());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I16, Clear)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I16);
|
||||
self->Clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
// Int32 array
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I32, Copy)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
|
||||
PARAM_POINTER(other, FDynArray_I32);
|
||||
*self = *other;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I32, Move)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
|
||||
PARAM_POINTER(other, FDynArray_I32);
|
||||
*self = std::move(*other);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I32, Find)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
|
||||
PARAM_INT(val);
|
||||
ACTION_RETURN_INT(self->Find(val));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I32, Push)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
|
||||
PARAM_INT(val);
|
||||
ACTION_RETURN_INT(self->Push(val));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I32, Pop)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
|
||||
ACTION_RETURN_BOOL(self->Pop());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I32, Delete)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
|
||||
PARAM_INT(index);
|
||||
PARAM_INT(count);
|
||||
self->Delete(index, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I32, Insert)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
|
||||
PARAM_INT(index);
|
||||
PARAM_INT(val);
|
||||
self->Insert(index, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I32, ShrinkToFit)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
|
||||
self->ShrinkToFit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I32, Grow)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
|
||||
PARAM_INT(count);
|
||||
self->Grow(count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I32, Resize)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
|
||||
PARAM_INT(count);
|
||||
self->Resize(count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I32, Reserve)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
|
||||
PARAM_INT(count);
|
||||
ACTION_RETURN_INT(self->Reserve(count));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I32, Max)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
|
||||
ACTION_RETURN_INT(self->Max());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_I32, Clear)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_I32);
|
||||
self->Clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
// Float32 array
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F32, Copy)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
|
||||
PARAM_POINTER(other, FDynArray_F32);
|
||||
*self = *other;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F32, Move)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
|
||||
PARAM_POINTER(other, FDynArray_F32);
|
||||
*self = std::move(*other);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F32, Find)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
|
||||
PARAM_FLOAT(val);
|
||||
ACTION_RETURN_INT(self->Find((float)val));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F32, Push)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
|
||||
PARAM_FLOAT(val);
|
||||
ACTION_RETURN_INT(self->Push((float)val));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F32, Pop)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
|
||||
ACTION_RETURN_BOOL(self->Pop());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F32, Delete)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
|
||||
PARAM_INT(index);
|
||||
PARAM_INT(count);
|
||||
self->Delete(index, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F32, Insert)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
|
||||
PARAM_INT(index);
|
||||
PARAM_FLOAT(val);
|
||||
self->Insert(index, (float)val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F32, ShrinkToFit)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
|
||||
self->ShrinkToFit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F32, Grow)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
|
||||
PARAM_INT(count);
|
||||
self->Grow(count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F32, Resize)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
|
||||
PARAM_INT(count);
|
||||
self->Resize(count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F32, Reserve)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
|
||||
PARAM_INT(count);
|
||||
ACTION_RETURN_INT(self->Reserve(count));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F32, Max)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
|
||||
ACTION_RETURN_INT(self->Max());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F32, Clear)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F32);
|
||||
self->Clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
// Float64 array
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F64, Copy)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
|
||||
PARAM_POINTER(other, FDynArray_F64);
|
||||
*self = *other;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F64, Move)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
|
||||
PARAM_POINTER(other, FDynArray_F64);
|
||||
*self = std::move(*other);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F64, Find)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
|
||||
PARAM_FLOAT(val);
|
||||
ACTION_RETURN_INT(self->Find(val));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F64, Push)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
|
||||
PARAM_FLOAT(val);
|
||||
ACTION_RETURN_INT(self->Push(val));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F64, Pop)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
|
||||
ACTION_RETURN_BOOL(self->Pop());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F64, Delete)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
|
||||
PARAM_INT(index);
|
||||
PARAM_INT(count);
|
||||
self->Delete(index, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F64, Insert)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
|
||||
PARAM_INT(index);
|
||||
PARAM_FLOAT(val);
|
||||
self->Insert(index, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F64, ShrinkToFit)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
|
||||
self->ShrinkToFit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F64, Grow)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
|
||||
PARAM_INT(count);
|
||||
self->Grow(count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F64, Resize)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
|
||||
PARAM_INT(count);
|
||||
self->Resize(count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F64, Reserve)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
|
||||
PARAM_INT(count);
|
||||
ACTION_RETURN_INT(self->Reserve(count));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F64, Max)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
|
||||
ACTION_RETURN_INT(self->Max());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_F64, Clear)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_F64);
|
||||
self->Clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
// Pointer array
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Copy)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
|
||||
PARAM_POINTER(other, FDynArray_Ptr);
|
||||
*self = *other;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Move)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
|
||||
PARAM_POINTER(other, FDynArray_Ptr);
|
||||
*self = std::move(*other);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Find)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
|
||||
PARAM_POINTER(val, void);
|
||||
ACTION_RETURN_INT(self->Find(val));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Push)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
|
||||
PARAM_POINTER(val, void);
|
||||
ACTION_RETURN_INT(self->Push(val));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Pop)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
|
||||
ACTION_RETURN_BOOL(self->Pop());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Delete)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
|
||||
PARAM_INT(index);
|
||||
PARAM_INT(count);
|
||||
self->Delete(index, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Insert)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
|
||||
PARAM_INT(index);
|
||||
PARAM_POINTER(val, void);
|
||||
self->Insert(index, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_Ptr, ShrinkToFit)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
|
||||
self->ShrinkToFit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Grow)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
|
||||
PARAM_INT(count);
|
||||
self->Grow(count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Resize)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
|
||||
PARAM_INT(count);
|
||||
self->Resize(count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Reserve)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
|
||||
PARAM_INT(count);
|
||||
ACTION_RETURN_INT(self->Reserve(count));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Max)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
|
||||
ACTION_RETURN_INT(self->Max());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_Ptr, Clear)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_Ptr);
|
||||
self->Clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------
|
||||
//
|
||||
// String array
|
||||
//
|
||||
//-----------------------------------------------------
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_String, Copy)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
|
||||
PARAM_POINTER(other, FDynArray_String);
|
||||
*self = *other;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_String, Move)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
|
||||
PARAM_POINTER(other, FDynArray_String);
|
||||
*self = std::move(*other);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_String, Find)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
|
||||
PARAM_STRING(val);
|
||||
ACTION_RETURN_INT(self->Find(val));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_String, Push)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
|
||||
PARAM_STRING(val);
|
||||
ACTION_RETURN_INT(self->Push(val));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_String, Pop)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
|
||||
ACTION_RETURN_BOOL(self->Pop());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_String, Delete)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
|
||||
PARAM_INT(index);
|
||||
PARAM_INT(count);
|
||||
self->Delete(index, count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_String, Insert)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
|
||||
PARAM_INT(index);
|
||||
PARAM_STRING(val);
|
||||
self->Insert(index, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_String, ShrinkToFit)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
|
||||
self->ShrinkToFit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_String, Grow)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
|
||||
PARAM_INT(count);
|
||||
self->Grow(count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_String, Resize)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
|
||||
PARAM_INT(count);
|
||||
self->Resize(count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_String, Reserve)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
|
||||
PARAM_INT(count);
|
||||
ACTION_RETURN_INT(self->Reserve(count));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_String, Max)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
|
||||
ACTION_RETURN_INT(self->Max());
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(FDynArray_String, Clear)
|
||||
{
|
||||
PARAM_SELF_STRUCT_PROLOGUE(FDynArray_String);
|
||||
self->Clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_FIELD_NAMED_X(DynArray_I8, FArray, Count, Size)
|
||||
DEFINE_FIELD_NAMED_X(DynArray_I16, FArray, Count, Size)
|
||||
DEFINE_FIELD_NAMED_X(DynArray_I32, FArray, Count, Size)
|
||||
DEFINE_FIELD_NAMED_X(DynArray_F32, FArray, Count, Size)
|
||||
DEFINE_FIELD_NAMED_X(DynArray_F64, FArray, Count, Size)
|
||||
DEFINE_FIELD_NAMED_X(DynArray_Ptr, FArray, Count, Size)
|
||||
DEFINE_FIELD_NAMED_X(DynArray_String, FArray, Count, Size)
|
||||
953
src/scripting/backend/vmbuilder.cpp
Normal file
953
src/scripting/backend/vmbuilder.cpp
Normal file
|
|
@ -0,0 +1,953 @@
|
|||
/*
|
||||
** vmbuilder.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright -2016 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. 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.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "vmbuilder.h"
|
||||
#include "codegen.h"
|
||||
#include "info.h"
|
||||
#include "m_argv.h"
|
||||
#include "thingdef.h"
|
||||
#include "doomerrors.h"
|
||||
|
||||
struct VMRemap
|
||||
{
|
||||
BYTE altOp, kReg, kType;
|
||||
};
|
||||
|
||||
|
||||
#define xx(op, name, mode, alt, kreg, ktype) {OP_##alt, kreg, ktype }
|
||||
VMRemap opRemap[NUM_OPS] = {
|
||||
#include "vmops.h"
|
||||
};
|
||||
#undef xx
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder - Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
VMFunctionBuilder::VMFunctionBuilder(int numimplicits)
|
||||
{
|
||||
MaxParam = 0;
|
||||
ActiveParam = 0;
|
||||
NumImplicits = numimplicits;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder - Destructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
VMFunctionBuilder::~VMFunctionBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: BeginStatement
|
||||
//
|
||||
// Records the start of a new statement.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::BeginStatement(FxExpression *stmt)
|
||||
{
|
||||
// pop empty statement records.
|
||||
while (LineNumbers.Size() > 0 && LineNumbers.Last().InstructionIndex == Code.Size()) LineNumbers.Pop();
|
||||
// only add a new entry if the line number differs.
|
||||
if (LineNumbers.Size() == 0 || stmt->ScriptPosition.ScriptLine != LineNumbers.Last().LineNumber)
|
||||
{
|
||||
FStatementInfo si = { (uint16_t)Code.Size(), (uint16_t)stmt->ScriptPosition.ScriptLine };
|
||||
LineNumbers.Push(si);
|
||||
}
|
||||
StatementStack.Push(stmt);
|
||||
}
|
||||
|
||||
void VMFunctionBuilder::EndStatement()
|
||||
{
|
||||
// pop empty statement records.
|
||||
while (LineNumbers.Size() > 0 && LineNumbers.Last().InstructionIndex == Code.Size()) LineNumbers.Pop();
|
||||
StatementStack.Pop();
|
||||
// Re-enter the previous statement.
|
||||
if (StatementStack.Size() > 0)
|
||||
{
|
||||
FStatementInfo si = { (uint16_t)Code.Size(), (uint16_t)StatementStack.Last()->ScriptPosition.ScriptLine };
|
||||
LineNumbers.Push(si);
|
||||
}
|
||||
}
|
||||
|
||||
void VMFunctionBuilder::MakeFunction(VMScriptFunction *func)
|
||||
{
|
||||
func->Alloc(Code.Size(), IntConstantList.Size(), FloatConstantList.Size(), StringConstantList.Size(), AddressConstantList.Size(), LineNumbers.Size());
|
||||
|
||||
// Copy code block.
|
||||
memcpy(func->Code, &Code[0], Code.Size() * sizeof(VMOP));
|
||||
memcpy(func->LineInfo, &LineNumbers[0], LineNumbers.Size() * sizeof(LineNumbers[0]));
|
||||
|
||||
// Create constant tables.
|
||||
if (IntConstantList.Size() > 0)
|
||||
{
|
||||
FillIntConstants(func->KonstD);
|
||||
}
|
||||
if (FloatConstantList.Size() > 0)
|
||||
{
|
||||
FillFloatConstants(func->KonstF);
|
||||
}
|
||||
if (AddressConstantList.Size() > 0)
|
||||
{
|
||||
FillAddressConstants(func->KonstA, func->KonstATags());
|
||||
}
|
||||
if (StringConstantList.Size() > 0)
|
||||
{
|
||||
FillStringConstants(func->KonstS);
|
||||
}
|
||||
|
||||
// Assign required register space.
|
||||
func->NumRegD = Registers[REGT_INT].MostUsed;
|
||||
func->NumRegF = Registers[REGT_FLOAT].MostUsed;
|
||||
func->NumRegA = Registers[REGT_POINTER].MostUsed;
|
||||
func->NumRegS = Registers[REGT_STRING].MostUsed;
|
||||
func->MaxParam = MaxParam;
|
||||
|
||||
// Technically, there's no reason why we can't end the function with
|
||||
// entries on the parameter stack, but it means the caller probably
|
||||
// did something wrong.
|
||||
assert(ActiveParam == 0);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: FillIntConstants
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::FillIntConstants(int *konst)
|
||||
{
|
||||
memcpy(konst, &IntConstantList[0], sizeof(int) * IntConstantList.Size());
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: FillFloatConstants
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::FillFloatConstants(double *konst)
|
||||
{
|
||||
memcpy(konst, &FloatConstantList[0], sizeof(double) * FloatConstantList.Size());
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: FillAddressConstants
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::FillAddressConstants(FVoidObj *konst, VM_ATAG *tags)
|
||||
{
|
||||
memcpy(konst, &AddressConstantList[0], sizeof(void*) * AddressConstantList.Size());
|
||||
memcpy(tags, &AtagConstantList[0], sizeof(VM_ATAG) * AtagConstantList.Size());
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: FillStringConstants
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::FillStringConstants(FString *konst)
|
||||
{
|
||||
for (auto &s : StringConstantList)
|
||||
{
|
||||
*konst++ = s;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: GetConstantInt
|
||||
//
|
||||
// Returns a constant register initialized with the given value.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
unsigned VMFunctionBuilder::GetConstantInt(int val)
|
||||
{
|
||||
unsigned int *locp = IntConstantMap.CheckKey(val);
|
||||
if (locp != NULL)
|
||||
{
|
||||
return *locp;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned loc = IntConstantList.Push(val);
|
||||
IntConstantMap.Insert(val, loc);
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: GetConstantFloat
|
||||
//
|
||||
// Returns a constant register initialized with the given value.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
unsigned VMFunctionBuilder::GetConstantFloat(double val)
|
||||
{
|
||||
unsigned *locp = FloatConstantMap.CheckKey(val);
|
||||
if (locp != NULL)
|
||||
{
|
||||
return *locp;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned loc = FloatConstantList.Push(val);
|
||||
FloatConstantMap.Insert(val, loc);
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: GetConstantString
|
||||
//
|
||||
// Returns a constant register initialized with the given value.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
unsigned VMFunctionBuilder::GetConstantString(FString val)
|
||||
{
|
||||
unsigned *locp = StringConstantMap.CheckKey(val);
|
||||
if (locp != NULL)
|
||||
{
|
||||
return *locp;
|
||||
}
|
||||
else
|
||||
{
|
||||
int loc = StringConstantList.Push(val);
|
||||
StringConstantMap.Insert(val, loc);
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: GetConstantAddress
|
||||
//
|
||||
// Returns a constant register initialized with the given value, or -1 if
|
||||
// there were no more constants free.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
unsigned VMFunctionBuilder::GetConstantAddress(void *ptr, VM_ATAG tag)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
{ // Make all NULL pointers generic. (Or should we allow typed NULLs?)
|
||||
tag = ATAG_GENERIC;
|
||||
}
|
||||
AddrKonst *locp = AddressConstantMap.CheckKey(ptr);
|
||||
if (locp != NULL)
|
||||
{
|
||||
// There should only be one tag associated with a memory location. Exceptions are made for null pointers that got allocated through constant arrays.
|
||||
assert(ptr == nullptr || locp->Tag == tag);
|
||||
return locp->KonstNum;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned locc = AddressConstantList.Push(ptr);
|
||||
AtagConstantList.Push(tag);
|
||||
|
||||
AddrKonst loc = { locc, tag };
|
||||
AddressConstantMap.Insert(ptr, loc);
|
||||
return loc.KonstNum;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: AllocConstants*
|
||||
//
|
||||
// Returns a range of constant register initialized with the given values.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
unsigned VMFunctionBuilder::AllocConstantsInt(unsigned count, int *values)
|
||||
{
|
||||
unsigned addr = IntConstantList.Reserve(count);
|
||||
memcpy(&IntConstantList[addr], values, count * sizeof(int));
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
{
|
||||
IntConstantMap.Insert(values[i], addr + i);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
unsigned VMFunctionBuilder::AllocConstantsFloat(unsigned count, double *values)
|
||||
{
|
||||
unsigned addr = FloatConstantList.Reserve(count);
|
||||
memcpy(&FloatConstantList[addr], values, count * sizeof(double));
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
{
|
||||
FloatConstantMap.Insert(values[i], addr + i);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
unsigned VMFunctionBuilder::AllocConstantsAddress(unsigned count, void **ptrs, VM_ATAG tag)
|
||||
{
|
||||
unsigned addr = AddressConstantList.Reserve(count);
|
||||
AtagConstantList.Reserve(count);
|
||||
memcpy(&AddressConstantList[addr], ptrs, count * sizeof(void *));
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
{
|
||||
AtagConstantList[addr + i] = tag;
|
||||
AddrKonst loc = { addr+i, tag };
|
||||
AddressConstantMap.Insert(ptrs[i], loc);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
unsigned VMFunctionBuilder::AllocConstantsString(unsigned count, FString *ptrs)
|
||||
{
|
||||
unsigned addr = StringConstantList.Reserve(count);
|
||||
for (unsigned i = 0; i < count; i++)
|
||||
{
|
||||
StringConstantList[addr + i] = ptrs[i];
|
||||
StringConstantMap.Insert(ptrs[i], addr + i);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: ParamChange
|
||||
//
|
||||
// Adds delta to ActiveParam and keeps track of MaxParam.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::ParamChange(int delta)
|
||||
{
|
||||
assert(delta > 0 || -delta <= ActiveParam);
|
||||
ActiveParam += delta;
|
||||
if (ActiveParam > MaxParam)
|
||||
{
|
||||
MaxParam = ActiveParam;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: RegAvailability - Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
VMFunctionBuilder::RegAvailability::RegAvailability()
|
||||
{
|
||||
memset(Used, 0, sizeof(Used));
|
||||
MostUsed = 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: RegAvailability :: Get
|
||||
//
|
||||
// Gets one or more unused registers. If getting multiple registers, they
|
||||
// will all be consecutive. Returns -1 if there were not enough consecutive
|
||||
// registers to satisfy the request.
|
||||
//
|
||||
// Preference is given to low-numbered registers in an attempt to keep
|
||||
// the maximum register count low so as to preserve VM stack space when this
|
||||
// function is executed.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int VMFunctionBuilder::RegAvailability::Get(int count)
|
||||
{
|
||||
VM_UWORD mask;
|
||||
int i, firstbit;
|
||||
|
||||
// Getting fewer than one register makes no sense, and
|
||||
// the algorithm used here can only obtain ranges of up to 32 bits.
|
||||
if (count < 1 || count > 32)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
mask = count == 32 ? ~0u : (1 << count) - 1;
|
||||
|
||||
for (i = 0; i < 256 / 32; ++i)
|
||||
{
|
||||
// Find the first word with free registers
|
||||
VM_UWORD bits = Used[i];
|
||||
if (bits != ~0u)
|
||||
{
|
||||
// Are there enough consecutive bits to satisfy the request?
|
||||
// Search by 16, then 8, then 1 bit at a time for the first
|
||||
// free register.
|
||||
if ((bits & 0xFFFF) == 0xFFFF)
|
||||
{
|
||||
firstbit = ((bits & 0xFF0000) == 0xFF0000) ? 24 : 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
firstbit = ((bits & 0xFF) == 0xFF) ? 8 : 0;
|
||||
}
|
||||
for (; firstbit < 32; ++firstbit)
|
||||
{
|
||||
if (((bits >> firstbit) & mask) == 0)
|
||||
{
|
||||
if (firstbit + count <= 32)
|
||||
{ // Needed bits all fit in one word, so we got it.
|
||||
if (i * 32 + firstbit + count > MostUsed)
|
||||
{
|
||||
MostUsed = i * 32 + firstbit + count;
|
||||
}
|
||||
Used[i] |= mask << firstbit;
|
||||
return i * 32 + firstbit;
|
||||
}
|
||||
// Needed bits span two words, so check the next word.
|
||||
else if (i < 256/32 - 1)
|
||||
{ // There is a next word.
|
||||
if (((Used[i + 1]) & (mask >> (32 - firstbit))) == 0)
|
||||
{ // The next word has the needed open space, too.
|
||||
if (i * 32 + firstbit + count > MostUsed)
|
||||
{
|
||||
MostUsed = i * 32 + firstbit + count;
|
||||
}
|
||||
Used[i] |= mask << firstbit;
|
||||
Used[i + 1] |= mask >> (32 - firstbit);
|
||||
return i * 32 + firstbit;
|
||||
}
|
||||
else
|
||||
{ // Skip to the next word, because we know we won't find
|
||||
// what we need if we stay inside this one. All bits
|
||||
// from firstbit to the end of the word are 0. If the
|
||||
// next word does not start with the x amount of 0's, we
|
||||
// need to satisfy the request, then it certainly won't
|
||||
// have the x+1 0's we would need if we started at
|
||||
// firstbit+1 in this one.
|
||||
firstbit = 32;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Out of words.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// No room!
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: RegAvailibity :: Return
|
||||
//
|
||||
// Marks a range of registers as free again.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::RegAvailability::Return(int reg, int count)
|
||||
{
|
||||
assert(count >= 1 && count <= 32);
|
||||
assert(reg >= 0 && reg + count <= 256);
|
||||
|
||||
VM_UWORD mask, partialmask;
|
||||
int firstword, firstbit;
|
||||
|
||||
mask = count == 32 ? ~0u : (1 << count) - 1;
|
||||
firstword = reg / 32;
|
||||
firstbit = reg & 31;
|
||||
|
||||
if (firstbit + count <= 32)
|
||||
{ // Range is all in one word.
|
||||
mask <<= firstbit;
|
||||
// If we are trying to return registers that are already free,
|
||||
// it probably means that the caller messed up somewhere.
|
||||
assert((Used[firstword] & mask) == mask);
|
||||
Used[firstword] &= ~mask;
|
||||
}
|
||||
else
|
||||
{ // Range is in two words.
|
||||
partialmask = mask << firstbit;
|
||||
assert((Used[firstword] & partialmask) == partialmask);
|
||||
Used[firstword] &= ~partialmask;
|
||||
|
||||
partialmask = mask >> (32 - firstbit);
|
||||
assert((Used[firstword + 1] & partialmask) == partialmask);
|
||||
Used[firstword + 1] &= ~partialmask;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: RegAvailability :: Reuse
|
||||
//
|
||||
// Marks an unused register as in-use. Returns false if the register is
|
||||
// already in use or true if it was successfully reused.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool VMFunctionBuilder::RegAvailability::Reuse(int reg)
|
||||
{
|
||||
assert(reg >= 0 && reg <= 255);
|
||||
assert(reg < MostUsed && "Attempt to reuse a register that was never used");
|
||||
|
||||
VM_UWORD mask = 1 << (reg & 31);
|
||||
int word = reg / 32;
|
||||
|
||||
if (Used[word] & mask)
|
||||
{ // It's already in use!
|
||||
return false;
|
||||
}
|
||||
Used[word] |= mask;
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: GetAddress
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t VMFunctionBuilder::GetAddress()
|
||||
{
|
||||
return Code.Size();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: Emit
|
||||
//
|
||||
// Just dumbly output an instruction. Returns instruction position, not
|
||||
// byte position. (Because all instructions are exactly four bytes long.)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t VMFunctionBuilder::Emit(int opcode, int opa, int opb, int opc)
|
||||
{
|
||||
static BYTE opcodes[] = { OP_LK, OP_LKF, OP_LKS, OP_LKP };
|
||||
|
||||
assert(opcode >= 0 && opcode < NUM_OPS);
|
||||
assert(opa >= 0);
|
||||
assert(opb >= 0);
|
||||
assert(opc >= 0);
|
||||
|
||||
|
||||
// The following were just asserts, meaning this would silently create broken code if there was an overflow
|
||||
// if this happened in a release build. Not good.
|
||||
// These are critical errors that need to be reported to the user.
|
||||
// In addition, the limit of 256 constants can easily be exceeded with arrays so this had to be extended to
|
||||
// 65535 by adding some checks here that map byte-limited instructions to alternatives that can handle larger indices.
|
||||
// (See vmops.h for the remapping info.)
|
||||
|
||||
// Note: OP_CMPS also needs treatment, but I do not expect constant overflow to become an issue with strings, so for now there is no handling.
|
||||
|
||||
if (opa > 255)
|
||||
{
|
||||
if (opRemap[opcode].kReg != 1 || opa > 32767)
|
||||
{
|
||||
I_Error("Register limit exceeded");
|
||||
}
|
||||
int regtype = opRemap[opcode].kType;
|
||||
ExpEmit emit(this, regtype);
|
||||
Emit(opcodes[regtype], emit.RegNum, opa);
|
||||
opcode = opRemap[opcode].altOp;
|
||||
opa = emit.RegNum;
|
||||
emit.Free(this);
|
||||
}
|
||||
if (opb > 255)
|
||||
{
|
||||
if (opRemap[opcode].kReg != 2 || opb > 32767)
|
||||
{
|
||||
I_Error("Register limit exceeded");
|
||||
}
|
||||
int regtype = opRemap[opcode].kType;
|
||||
ExpEmit emit(this, regtype);
|
||||
Emit(opcodes[regtype], emit.RegNum, opb);
|
||||
opcode = opRemap[opcode].altOp;
|
||||
opb = emit.RegNum;
|
||||
emit.Free(this);
|
||||
}
|
||||
if (opc > 255)
|
||||
{
|
||||
if (opcode == OP_PARAM && (opb & REGT_KONST) && opc <= 32767)
|
||||
{
|
||||
int regtype = opb & REGT_TYPE;
|
||||
opb = regtype;
|
||||
ExpEmit emit(this, regtype);
|
||||
Emit(opcodes[regtype], emit.RegNum, opc);
|
||||
opc = emit.RegNum;
|
||||
emit.Free(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (opRemap[opcode].kReg != 4 || opc > 32767)
|
||||
{
|
||||
I_Error("Register limit exceeded");
|
||||
}
|
||||
int regtype = opRemap[opcode].kType;
|
||||
ExpEmit emit(this, regtype);
|
||||
Emit(opcodes[regtype], emit.RegNum, opc);
|
||||
opcode = opRemap[opcode].altOp;
|
||||
opc = emit.RegNum;
|
||||
emit.Free(this);
|
||||
}
|
||||
}
|
||||
|
||||
if (opcode == OP_PARAM)
|
||||
{
|
||||
int chg;
|
||||
if (opb & REGT_MULTIREG2) chg = 2;
|
||||
else if (opb®T_MULTIREG3) chg = 3;
|
||||
else chg = 1;
|
||||
ParamChange(chg);
|
||||
}
|
||||
else if (opcode == OP_CALL || opcode == OP_CALL_K || opcode == OP_TAIL || opcode == OP_TAIL_K)
|
||||
{
|
||||
ParamChange(-opb);
|
||||
}
|
||||
VMOP op;
|
||||
op.op = opcode;
|
||||
op.a = opa;
|
||||
op.b = opb;
|
||||
op.c = opc;
|
||||
return Code.Push(op);
|
||||
}
|
||||
|
||||
size_t VMFunctionBuilder::Emit(int opcode, int opa, VM_SHALF opbc)
|
||||
{
|
||||
assert(opcode >= 0 && opcode < NUM_OPS);
|
||||
assert(opa >= 0 && opa <= 255);
|
||||
//assert(opbc >= -32768 && opbc <= 32767); always true due to parameter's width
|
||||
VMOP op;
|
||||
op.op = opcode;
|
||||
op.a = opa;
|
||||
op.i16 = opbc;
|
||||
return Code.Push(op);
|
||||
}
|
||||
|
||||
size_t VMFunctionBuilder::Emit(int opcode, int opabc)
|
||||
{
|
||||
assert(opcode >= 0 && opcode < NUM_OPS);
|
||||
assert(opabc >= -(1 << 23) && opabc <= (1 << 24) - 1);
|
||||
if (opcode == OP_PARAMI)
|
||||
{
|
||||
ParamChange(1);
|
||||
}
|
||||
VMOP op;
|
||||
op.op = opcode;
|
||||
op.i24 = opabc;
|
||||
return Code.Push(op);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: EmitParamInt
|
||||
//
|
||||
// Passes a constant integer parameter, using either PARAMI and an immediate
|
||||
// value or PARAM and a constant register, as appropriate.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t VMFunctionBuilder::EmitParamInt(int value)
|
||||
{
|
||||
// Immediates for PARAMI must fit in 24 bits.
|
||||
if (((value << 8) >> 8) == value)
|
||||
{
|
||||
return Emit(OP_PARAMI, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Emit(OP_PARAM, 0, REGT_INT | REGT_KONST, GetConstantInt(value));
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: EmitLoadInt
|
||||
//
|
||||
// Loads an integer constant into a register, using either an immediate
|
||||
// value or a constant register, as appropriate.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t VMFunctionBuilder::EmitLoadInt(int regnum, int value)
|
||||
{
|
||||
assert(regnum >= 0 && regnum < Registers[REGT_INT].MostUsed);
|
||||
if (value >= -32768 && value <= 32767)
|
||||
{
|
||||
return Emit(OP_LI, regnum, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Emit(OP_LK, regnum, GetConstantInt(value));
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: EmitRetInt
|
||||
//
|
||||
// Returns an integer, using either an immediate value or a constant
|
||||
// register, as appropriate.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t VMFunctionBuilder::EmitRetInt(int retnum, bool final, int value)
|
||||
{
|
||||
assert(retnum >= 0 && retnum <= 127);
|
||||
if (value >= -32768 && value <= 32767)
|
||||
{
|
||||
return Emit(OP_RETI, retnum | (final << 7), value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Emit(OP_RET, retnum | (final << 7), REGT_INT | REGT_KONST, GetConstantInt(value));
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: Backpatch
|
||||
//
|
||||
// Store a JMP instruction at <loc> that points at <target>.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::Backpatch(size_t loc, size_t target)
|
||||
{
|
||||
assert(loc < Code.Size());
|
||||
int offset = int(target - loc - 1);
|
||||
assert(((offset << 8) >> 8) == offset);
|
||||
Code[loc].op = OP_JMP;
|
||||
Code[loc].i24 = offset;
|
||||
}
|
||||
|
||||
void VMFunctionBuilder::BackpatchList(TArray<size_t> &locs, size_t target)
|
||||
{
|
||||
for (auto loc : locs)
|
||||
Backpatch(loc, target);
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: BackpatchToHere
|
||||
//
|
||||
// Store a JMP instruction at <loc> that points to the current code gen
|
||||
// location.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::BackpatchToHere(size_t loc)
|
||||
{
|
||||
Backpatch(loc, Code.Size());
|
||||
}
|
||||
|
||||
void VMFunctionBuilder::BackpatchListToHere(TArray<size_t> &locs)
|
||||
{
|
||||
for (auto loc : locs)
|
||||
Backpatch(loc, Code.Size());
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// FFunctionBuildList
|
||||
//
|
||||
// This list contains all functions yet to build.
|
||||
// All adding functions return a VMFunction - either a complete one
|
||||
// for native functions or an empty VMScriptFunction for scripted ones
|
||||
// This VMScriptFunction object later gets filled in with the actual
|
||||
// info, but we get the pointer right after registering the function
|
||||
// with the builder.
|
||||
//
|
||||
//==========================================================================
|
||||
FFunctionBuildList FunctionBuildList;
|
||||
|
||||
VMFunction *FFunctionBuildList::AddFunction(PNamespace *gnspc, PFunction *functype, FxExpression *code, const FString &name, bool fromdecorate, int stateindex, int statecount, int lumpnum)
|
||||
{
|
||||
auto func = code->GetDirectFunction();
|
||||
if (func != nullptr)
|
||||
{
|
||||
delete code;
|
||||
return func;
|
||||
}
|
||||
|
||||
//Printf("Adding %s\n", name.GetChars());
|
||||
|
||||
Item it;
|
||||
assert(gnspc != nullptr);
|
||||
it.CurGlobals = gnspc;
|
||||
it.Func = functype;
|
||||
it.Code = code;
|
||||
it.PrintableName = name;
|
||||
it.Function = new VMScriptFunction;
|
||||
it.Function->Name = functype->SymbolName;
|
||||
it.Function->PrintableName = name;
|
||||
it.Function->ImplicitArgs = functype->GetImplicitArgs();
|
||||
it.Proto = nullptr;
|
||||
it.FromDecorate = fromdecorate;
|
||||
it.StateIndex = stateindex;
|
||||
it.StateCount = statecount;
|
||||
it.Lump = lumpnum;
|
||||
assert(it.Func->Variants.Size() == 1);
|
||||
it.Func->Variants[0].Implementation = it.Function;
|
||||
|
||||
// set prototype for named functions.
|
||||
if (it.Func->SymbolName != NAME_None)
|
||||
{
|
||||
it.Function->Proto = it.Func->Variants[0].Proto;
|
||||
}
|
||||
|
||||
mItems.Push(it);
|
||||
return it.Function;
|
||||
}
|
||||
|
||||
|
||||
void FFunctionBuildList::Build()
|
||||
{
|
||||
int errorcount = 0;
|
||||
int codesize = 0;
|
||||
FILE *dump = nullptr;
|
||||
|
||||
if (Args->CheckParm("-dumpdisasm")) dump = fopen("disasm.txt", "w");
|
||||
|
||||
for (auto &item : mItems)
|
||||
{
|
||||
assert(item.Code != NULL);
|
||||
|
||||
// We don't know the return type in advance for anonymous functions.
|
||||
FCompileContext ctx(item.CurGlobals, item.Func, item.Func->SymbolName == NAME_None ? nullptr : item.Func->Variants[0].Proto, item.FromDecorate, item.StateIndex, item.StateCount, item.Lump);
|
||||
|
||||
// Allocate registers for the function's arguments and create local variable nodes before starting to resolve it.
|
||||
VMFunctionBuilder buildit(item.Func->GetImplicitArgs());
|
||||
for (unsigned i = 0; i < item.Func->Variants[0].Proto->ArgumentTypes.Size(); i++)
|
||||
{
|
||||
auto type = item.Func->Variants[0].Proto->ArgumentTypes[i];
|
||||
auto name = item.Func->Variants[0].ArgNames[i];
|
||||
auto flags = item.Func->Variants[0].ArgFlags[i];
|
||||
// this won't get resolved and won't get emitted. It is only needed so that the code generator can retrieve the necessary info about this argument to do its work.
|
||||
auto local = new FxLocalVariableDeclaration(type, name, nullptr, flags, FScriptPosition());
|
||||
if (!(flags & VARF_Out)) local->RegNum = buildit.Registers[type->GetRegType()].Get(type->GetRegCount());
|
||||
else local->RegNum = buildit.Registers[REGT_POINTER].Get(1);
|
||||
ctx.FunctionArgs.Push(local);
|
||||
}
|
||||
|
||||
FScriptPosition::StrictErrors = !item.FromDecorate;
|
||||
item.Code = item.Code->Resolve(ctx);
|
||||
// If we need extra space, load the frame pointer into a register so that we do not have to call the wasteful LFP instruction more than once.
|
||||
if (item.Function->ExtraSpace > 0)
|
||||
{
|
||||
buildit.FramePointer = ExpEmit(&buildit, REGT_POINTER);
|
||||
buildit.FramePointer.Fixed = true;
|
||||
buildit.Emit(OP_LFP, buildit.FramePointer.RegNum);
|
||||
}
|
||||
|
||||
// Make sure resolving it didn't obliterate it.
|
||||
if (item.Code != nullptr)
|
||||
{
|
||||
if (!item.Code->CheckReturn())
|
||||
{
|
||||
auto newcmpd = new FxCompoundStatement(item.Code->ScriptPosition);
|
||||
newcmpd->Add(item.Code);
|
||||
newcmpd->Add(new FxReturnStatement(nullptr, item.Code->ScriptPosition));
|
||||
item.Code = newcmpd->Resolve(ctx);
|
||||
}
|
||||
|
||||
item.Proto = ctx.ReturnProto;
|
||||
if (item.Proto == nullptr)
|
||||
{
|
||||
item.Code->ScriptPosition.Message(MSG_ERROR, "Function %s without prototype", item.PrintableName.GetChars());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Generate prototype for anonymous functions.
|
||||
VMScriptFunction *sfunc = item.Function;
|
||||
// create a new prototype from the now known return type and the argument list of the function's template prototype.
|
||||
if (sfunc->Proto == nullptr)
|
||||
{
|
||||
sfunc->Proto = NewPrototype(item.Proto->ReturnTypes, item.Func->Variants[0].Proto->ArgumentTypes);
|
||||
}
|
||||
|
||||
// Emit code
|
||||
try
|
||||
{
|
||||
sfunc->SourceFileName = item.Code->ScriptPosition.FileName; // remember the file name for printing error messages if something goes wrong in the VM.
|
||||
buildit.BeginStatement(item.Code);
|
||||
item.Code->Emit(&buildit);
|
||||
buildit.EndStatement();
|
||||
buildit.MakeFunction(sfunc);
|
||||
sfunc->NumArgs = 0;
|
||||
// NumArgs for the VMFunction must be the amount of stack elements, which can differ from the amount of logical function arguments if vectors are in the list.
|
||||
// For the VM a vector is 2 or 3 args, depending on size.
|
||||
for (auto s : item.Func->Variants[0].Proto->ArgumentTypes)
|
||||
{
|
||||
sfunc->NumArgs += s->GetRegCount();
|
||||
}
|
||||
|
||||
if (dump != nullptr)
|
||||
{
|
||||
DumpFunction(dump, sfunc, item.PrintableName.GetChars(), (int)item.PrintableName.Len());
|
||||
codesize += sfunc->CodeSize;
|
||||
}
|
||||
sfunc->Unsafe = ctx.Unsafe;
|
||||
}
|
||||
catch (CRecoverableError &err)
|
||||
{
|
||||
// catch errors from the code generator and pring something meaningful.
|
||||
item.Code->ScriptPosition.Message(MSG_ERROR, "%s in %s", err.GetMessage(), item.PrintableName.GetChars());
|
||||
}
|
||||
}
|
||||
delete item.Code;
|
||||
if (dump != nullptr)
|
||||
{
|
||||
fflush(dump);
|
||||
}
|
||||
}
|
||||
if (dump != nullptr)
|
||||
{
|
||||
fprintf(dump, "\n*************************************************************************\n%i code bytes\n", codesize * 4);
|
||||
fclose(dump);
|
||||
}
|
||||
FScriptPosition::StrictErrors = false;
|
||||
mItems.Clear();
|
||||
FxAlloc.FreeAllBlocks();
|
||||
}
|
||||
160
src/scripting/backend/vmbuilder.h
Normal file
160
src/scripting/backend/vmbuilder.h
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
#ifndef VMUTIL_H
|
||||
#define VMUTIL_H
|
||||
|
||||
#include "dobject.h"
|
||||
|
||||
class VMFunctionBuilder;
|
||||
class FxExpression;
|
||||
class FxLocalVariableDeclaration;
|
||||
|
||||
struct ExpEmit
|
||||
{
|
||||
ExpEmit() : RegNum(0), RegType(REGT_NIL), RegCount(1), Konst(false), Fixed(false), Final(false), Target(false) {}
|
||||
ExpEmit(int reg, int type, bool konst = false, bool fixed = false) : RegNum(reg), RegType(type), RegCount(1), Konst(konst), Fixed(fixed), Final(false), Target(false) {}
|
||||
ExpEmit(VMFunctionBuilder *build, int type, int count = 1);
|
||||
void Free(VMFunctionBuilder *build);
|
||||
void Reuse(VMFunctionBuilder *build);
|
||||
|
||||
uint16_t RegNum;
|
||||
uint8_t RegType, RegCount;
|
||||
// We are at 8 bytes for this struct, no matter what, so it's rather pointless to squeeze these flags into bitfields.
|
||||
bool Konst, Fixed, Final, Target;
|
||||
};
|
||||
|
||||
class VMFunctionBuilder
|
||||
{
|
||||
public:
|
||||
// Keeps track of which registers are available by way of a bitmask table.
|
||||
class RegAvailability
|
||||
{
|
||||
public:
|
||||
RegAvailability();
|
||||
int GetMostUsed() { return MostUsed; }
|
||||
int Get(int count); // Returns the first register in the range
|
||||
void Return(int reg, int count);
|
||||
bool Reuse(int regnum);
|
||||
|
||||
private:
|
||||
VM_UWORD Used[256/32]; // Bitmap of used registers (bit set means reg is used)
|
||||
int MostUsed;
|
||||
|
||||
friend class VMFunctionBuilder;
|
||||
};
|
||||
|
||||
VMFunctionBuilder(int numimplicits);
|
||||
~VMFunctionBuilder();
|
||||
|
||||
void BeginStatement(FxExpression *stmt);
|
||||
void EndStatement();
|
||||
void MakeFunction(VMScriptFunction *func);
|
||||
|
||||
// Returns the constant register holding the value.
|
||||
unsigned GetConstantInt(int val);
|
||||
unsigned GetConstantFloat(double val);
|
||||
unsigned GetConstantAddress(void *ptr, VM_ATAG tag);
|
||||
unsigned GetConstantString(FString str);
|
||||
|
||||
unsigned AllocConstantsInt(unsigned int count, int *values);
|
||||
unsigned AllocConstantsFloat(unsigned int count, double *values);
|
||||
unsigned AllocConstantsAddress(unsigned int count, void **ptrs, VM_ATAG tag);
|
||||
unsigned AllocConstantsString(unsigned int count, FString *ptrs);
|
||||
|
||||
|
||||
// Returns the address of the next instruction to be emitted.
|
||||
size_t GetAddress();
|
||||
|
||||
// Returns the address of the newly-emitted instruction.
|
||||
size_t Emit(int opcode, int opa, int opb, int opc);
|
||||
size_t Emit(int opcode, int opa, VM_SHALF opbc);
|
||||
size_t Emit(int opcode, int opabc);
|
||||
size_t EmitParamInt(int value);
|
||||
size_t EmitLoadInt(int regnum, int value);
|
||||
size_t EmitRetInt(int retnum, bool final, int value);
|
||||
|
||||
void Backpatch(size_t addr, size_t target);
|
||||
void BackpatchToHere(size_t addr);
|
||||
void BackpatchList(TArray<size_t> &addrs, size_t target);
|
||||
void BackpatchListToHere(TArray<size_t> &addrs);
|
||||
|
||||
// Write out complete constant tables.
|
||||
void FillIntConstants(int *konst);
|
||||
void FillFloatConstants(double *konst);
|
||||
void FillAddressConstants(FVoidObj *konst, VM_ATAG *tags);
|
||||
void FillStringConstants(FString *strings);
|
||||
|
||||
// PARAM increases ActiveParam; CALL decreases it.
|
||||
void ParamChange(int delta);
|
||||
|
||||
// Track available registers.
|
||||
RegAvailability Registers[4];
|
||||
|
||||
// amount of implicit parameters so that proper code can be emitted for method calls
|
||||
int NumImplicits;
|
||||
|
||||
// keep the frame pointer, if needed, in a register because the LFP opcode is hideously inefficient, requiring more than 20 instructions on x64.
|
||||
ExpEmit FramePointer;
|
||||
TArray<FxLocalVariableDeclaration *> ConstructedStructs;
|
||||
|
||||
private:
|
||||
struct AddrKonst
|
||||
{
|
||||
unsigned KonstNum;
|
||||
VM_ATAG Tag;
|
||||
};
|
||||
|
||||
TArray<FStatementInfo> LineNumbers;
|
||||
TArray<FxExpression *> StatementStack;
|
||||
|
||||
TArray<int> IntConstantList;
|
||||
TArray<double> FloatConstantList;
|
||||
TArray<void *> AddressConstantList;
|
||||
TArray<VM_ATAG> AtagConstantList;
|
||||
TArray<FString> StringConstantList;
|
||||
// These map from the constant value to its position in the constant table.
|
||||
TMap<int, unsigned> IntConstantMap;
|
||||
TMap<double, unsigned> FloatConstantMap;
|
||||
TMap<void *, AddrKonst> AddressConstantMap;
|
||||
TMap<FString, unsigned> StringConstantMap;
|
||||
|
||||
int MaxParam;
|
||||
int ActiveParam;
|
||||
|
||||
TArray<VMOP> Code;
|
||||
|
||||
};
|
||||
|
||||
void DumpFunction(FILE *dump, VMScriptFunction *sfunc, const char *label, int labellen);
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
class FxExpression;
|
||||
|
||||
class FFunctionBuildList
|
||||
{
|
||||
struct Item
|
||||
{
|
||||
PFunction *Func = nullptr;
|
||||
FxExpression *Code = nullptr;
|
||||
PPrototype *Proto = nullptr;
|
||||
VMScriptFunction *Function = nullptr;
|
||||
PNamespace *CurGlobals = nullptr;
|
||||
FString PrintableName;
|
||||
int StateIndex;
|
||||
int StateCount;
|
||||
int Lump;
|
||||
bool FromDecorate;
|
||||
};
|
||||
|
||||
TArray<Item> mItems;
|
||||
|
||||
public:
|
||||
VMFunction *AddFunction(PNamespace *curglobals, PFunction *func, FxExpression *code, const FString &name, bool fromdecorate, int currentstate, int statecnt, int lumpnum);
|
||||
void Build();
|
||||
};
|
||||
|
||||
extern FFunctionBuildList FunctionBuildList;
|
||||
#endif
|
||||
653
src/scripting/backend/vmdisasm.cpp
Normal file
653
src/scripting/backend/vmdisasm.cpp
Normal file
|
|
@ -0,0 +1,653 @@
|
|||
/*
|
||||
** vmdisasm.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright -2016 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. 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.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "dobject.h"
|
||||
#include "c_console.h"
|
||||
#include "templates.h"
|
||||
|
||||
#define NOP MODE_AUNUSED | MODE_BUNUSED | MODE_CUNUSED
|
||||
|
||||
#define LI MODE_AI | MODE_BCJOINT | MODE_BCIMMS
|
||||
#define LKI MODE_AI | MODE_BCJOINT | MODE_BCKI
|
||||
#define LKF MODE_AF | MODE_BCJOINT | MODE_BCKF
|
||||
#define LKS MODE_AS | MODE_BCJOINT | MODE_BCKS
|
||||
#define LKP MODE_AP | MODE_BCJOINT | MODE_BCKP
|
||||
#define LFP MODE_AP | MODE_BUNUSED | MODE_CUNUSED
|
||||
|
||||
#define RIRPKI MODE_AI | MODE_BP | MODE_CKI
|
||||
#define RIRPRI MODE_AI | MODE_BP | MODE_CI
|
||||
#define RFRPKI MODE_AF | MODE_BP | MODE_CKI
|
||||
#define RFRPRI MODE_AF | MODE_BP | MODE_CI
|
||||
#define RSRPKI MODE_AS | MODE_BP | MODE_CKI
|
||||
#define RSRPRI MODE_AS | MODE_BP | MODE_CI
|
||||
#define RPRPKI MODE_AP | MODE_BP | MODE_CKI
|
||||
#define RPRPRI MODE_AP | MODE_BP | MODE_CI
|
||||
#define RVRPKI MODE_AV | MODE_BP | MODE_CKI
|
||||
#define RVRPRI MODE_AV | MODE_BP | MODE_CI
|
||||
#define RIRPI8 MODE_AI | MODE_BP | MODE_CIMMZ
|
||||
|
||||
#define RPRIKI MODE_AP | MODE_BI | MODE_CKI
|
||||
#define RPRIRI MODE_AP | MODE_BI | MODE_CI
|
||||
#define RPRFKI MODE_AP | MODE_BF | MODE_CKI
|
||||
#define RPRFRI MODE_AP | MODE_BF | MODE_CI
|
||||
#define RPRSKI MODE_AP | MODE_BS | MODE_CKI
|
||||
#define RPRSRI MODE_AP | MODE_BS | MODE_CI
|
||||
#define RPRPKI MODE_AP | MODE_BP | MODE_CKI
|
||||
#define RPRPRI MODE_AP | MODE_BP | MODE_CI
|
||||
#define RPRVKI MODE_AP | MODE_BV | MODE_CKI
|
||||
#define RPRVRI MODE_AP | MODE_BV | MODE_CI
|
||||
#define RPRII8 MODE_AP | MODE_BI | MODE_CIMMZ
|
||||
|
||||
#define RIRI MODE_AI | MODE_BI | MODE_CUNUSED
|
||||
#define RFRF MODE_AF | MODE_BF | MODE_CUNUSED
|
||||
#define RSRS MODE_AS | MODE_BS | MODE_CUNUSED
|
||||
#define RPRP MODE_AP | MODE_BP | MODE_CUNUSED
|
||||
#define RPKP MODE_AP | MODE_BKP | MODE_CUNUSED
|
||||
#define RXRXI8 MODE_AX | MODE_BX | MODE_CIMMZ
|
||||
#define RPRPRP MODE_AP | MODE_BP | MODE_CP
|
||||
#define RPRPKP MODE_AP | MODE_BP | MODE_CKP
|
||||
|
||||
#define RII16 MODE_AI | MODE_BCJOINT | MODE_BCIMMS
|
||||
#define I24 MODE_ABCJOINT
|
||||
#define I8 MODE_AIMMZ | MODE_BUNUSED | MODE_CUNUSED
|
||||
#define I8I16 MODE_AIMMZ | MODE_BCIMMZ
|
||||
#define __BCP MODE_AUNUSED | MODE_BCJOINT | MODE_BCPARAM
|
||||
#define RPI8 MODE_AP | MODE_BIMMZ | MODE_CUNUSED
|
||||
#define KPI8 MODE_AKP | MODE_BIMMZ | MODE_CUNUSED
|
||||
#define RPI8I8 MODE_AP | MODE_BIMMZ | MODE_CIMMZ
|
||||
#define RPRPI8 MODE_AP | MODE_BP | MODE_CIMMZ
|
||||
#define KPI8I8 MODE_AKP | MODE_BIMMZ | MODE_CIMMZ
|
||||
#define I8BCP MODE_AIMMZ | MODE_BCJOINT | MODE_BCPARAM
|
||||
#define THROW MODE_AIMMZ | MODE_BCTHROW
|
||||
#define CATCH MODE_AIMMZ | MODE_BCCATCH
|
||||
#define CAST MODE_AX | MODE_BX | MODE_CIMMZ | MODE_BCCAST
|
||||
#define CASTB MODE_AI | MODE_BX | MODE_CIMMZ | MODE_BCCAST
|
||||
|
||||
#define RSRSRS MODE_AS | MODE_BS | MODE_CS
|
||||
#define RIRS MODE_AI | MODE_BS | MODE_CUNUSED
|
||||
#define I8RXRX MODE_AIMMZ | MODE_BX | MODE_CX
|
||||
|
||||
#define RIRIRI MODE_AI | MODE_BI | MODE_CI
|
||||
#define RIRII8 MODE_AI | MODE_BI | MODE_CIMMZ
|
||||
#define RFRII8 MODE_AF | MODE_BI | MODE_CIMMZ
|
||||
#define RPRII8 MODE_AP | MODE_BI | MODE_CIMMZ
|
||||
#define RSRII8 MODE_AS | MODE_BI | MODE_CIMMZ
|
||||
#define RIRIKI MODE_AI | MODE_BI | MODE_CKI
|
||||
#define RIKIRI MODE_AI | MODE_BKI | MODE_CI
|
||||
#define RIKII8 MODE_AI | MODE_BKI | MODE_CIMMZ
|
||||
#define RIRIIs MODE_AI | MODE_BI | MODE_CIMMS
|
||||
#define I8RIRI MODE_AIMMZ | MODE_BI | MODE_CI
|
||||
#define I8RIKI MODE_AIMMZ | MODE_BI | MODE_CKI
|
||||
#define I8KIRI MODE_AIMMZ | MODE_BKI | MODE_CI
|
||||
|
||||
#define RFRFRF MODE_AF | MODE_BF | MODE_CF
|
||||
#define RFRFKF MODE_AF | MODE_BF | MODE_CKF
|
||||
#define RFKFRF MODE_AF | MODE_BKF | MODE_CF
|
||||
#define I8RFRF MODE_AIMMZ | MODE_BF | MODE_CF
|
||||
#define I8RFKF MODE_AIMMZ | MODE_BF | MODE_CKF
|
||||
#define I8KFRF MODE_AIMMZ | MODE_BKF | MODE_CF
|
||||
#define RFRFI8 MODE_AF | MODE_BF | MODE_CIMMZ
|
||||
|
||||
#define RVRV MODE_AV | MODE_BV | MODE_CUNUSED
|
||||
#define RVRVRV MODE_AV | MODE_BV | MODE_CV
|
||||
#define RVRVKV MODE_AV | MODE_BV | MODE_CKV
|
||||
#define RVKVRV MODE_AV | MODE_BKV | MODE_CV
|
||||
#define RVRVRF MODE_AV | MODE_BV | MODE_CF
|
||||
#define RVRVKF MODE_AV | MODE_BV | MODE_CKF
|
||||
#define RVKVRF MODE_AV | MODE_BKV | MODE_CF
|
||||
#define RFRV MODE_AF | MODE_BV | MODE_CUNUSED
|
||||
#define I8RVRV MODE_AIMMZ | MODE_BV | MODE_CV
|
||||
#define I8RVKV MODE_AIMMZ | MODE_BV | MODE_CKV
|
||||
|
||||
#define RPRPRI MODE_AP | MODE_BP | MODE_CI
|
||||
#define RPRPKI MODE_AP | MODE_BP | MODE_CKI
|
||||
#define RIRPRP MODE_AI | MODE_BP | MODE_CP
|
||||
#define I8RPRP MODE_AIMMZ | MODE_BP | MODE_CP
|
||||
#define I8RPKP MODE_AIMMZ | MODE_BP | MODE_CKP
|
||||
|
||||
#define CIRR MODE_ACMP | MODE_BI | MODE_CI
|
||||
#define CIRK MODE_ACMP | MODE_BI | MODE_CKI
|
||||
#define CIKR MODE_ACMP | MODE_BKI | MODE_CI
|
||||
#define CFRR MODE_ACMP | MODE_BF | MODE_CF
|
||||
#define CFRK MODE_ACMP | MODE_BF | MODE_CKF
|
||||
#define CFKR MODE_ACMP | MODE_BKF | MODE_CF
|
||||
#define CVRR MODE_ACMP | MODE_BV | MODE_CV
|
||||
#define CVRK MODE_ACMP | MODE_BV | MODE_CKV
|
||||
#define CPRR MODE_ACMP | MODE_BP | MODE_CP
|
||||
#define CPRK MODE_ACMP | MODE_BP | MODE_CKP
|
||||
|
||||
const VMOpInfo OpInfo[NUM_OPS] =
|
||||
{
|
||||
#define xx(op, name, mode, alt, kreg, ktype) { #name, mode }
|
||||
#include "vmops.h"
|
||||
};
|
||||
|
||||
static const char *const FlopNames[] =
|
||||
{
|
||||
"abs",
|
||||
"neg",
|
||||
"exp",
|
||||
"log",
|
||||
"log10",
|
||||
"sqrt",
|
||||
"ceil",
|
||||
"floor",
|
||||
|
||||
"acos rad",
|
||||
"asin rad",
|
||||
"atan rad",
|
||||
"cos rad",
|
||||
"sin rad",
|
||||
"tan rad",
|
||||
|
||||
"acos deg",
|
||||
"asin deg",
|
||||
"atan deg",
|
||||
"cos deg",
|
||||
"sin deg",
|
||||
"tan deg",
|
||||
|
||||
"cosh",
|
||||
"sinh",
|
||||
"tanh",
|
||||
};
|
||||
|
||||
static int print_reg(FILE *out, int col, int arg, int mode, int immshift, const VMScriptFunction *func);
|
||||
|
||||
static int printf_wrapper(FILE *f, const char *fmt, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
int count;
|
||||
|
||||
va_start(argptr, fmt);
|
||||
if (f == NULL)
|
||||
{
|
||||
count = VPrintf(PRINT_HIGH, fmt, argptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
count = vfprintf(f, fmt, argptr);
|
||||
}
|
||||
va_end(argptr);
|
||||
return count;
|
||||
}
|
||||
|
||||
void VMDumpConstants(FILE *out, const VMScriptFunction *func)
|
||||
{
|
||||
char tmp[30];
|
||||
int i, j, k, kk;
|
||||
|
||||
if (func->KonstD != NULL && func->NumKonstD != 0)
|
||||
{
|
||||
printf_wrapper(out, "\nConstant integers:\n");
|
||||
kk = (func->NumKonstD + 3) / 4;
|
||||
for (i = 0; i < kk; ++i)
|
||||
{
|
||||
for (j = 0, k = i; j < 4 && k < func->NumKonstD; j++, k += kk)
|
||||
{
|
||||
mysnprintf(tmp, countof(tmp), "%3d. %d", k, func->KonstD[k]);
|
||||
printf_wrapper(out, "%-20s", tmp);
|
||||
}
|
||||
printf_wrapper(out, "\n");
|
||||
}
|
||||
}
|
||||
if (func->KonstF != NULL && func->NumKonstF != 0)
|
||||
{
|
||||
printf_wrapper(out, "\nConstant floats:\n");
|
||||
kk = (func->NumKonstF + 3) / 4;
|
||||
for (i = 0; i < kk; ++i)
|
||||
{
|
||||
for (j = 0, k = i; j < 4 && k < func->NumKonstF; j++, k += kk)
|
||||
{
|
||||
mysnprintf(tmp, countof(tmp), "%3d. %.16f", k, func->KonstF[k]);
|
||||
printf_wrapper(out, "%-20s", tmp);
|
||||
}
|
||||
printf_wrapper(out, "\n");
|
||||
}
|
||||
}
|
||||
if (func->KonstA != NULL && func->NumKonstA != 0)
|
||||
{
|
||||
printf_wrapper(out, "\nConstant addresses:\n");
|
||||
kk = (func->NumKonstA + 3) / 4;
|
||||
for (i = 0; i < kk; ++i)
|
||||
{
|
||||
for (j = 0, k = i; j < 4 && k < func->NumKonstA; j++, k += kk)
|
||||
{
|
||||
mysnprintf(tmp, countof(tmp), "%3d. %p:%d", k, func->KonstA[k].v, func->KonstATags()[k]);
|
||||
printf_wrapper(out, "%-22s", tmp);
|
||||
}
|
||||
printf_wrapper(out, "\n");
|
||||
}
|
||||
}
|
||||
if (func->KonstS != NULL && func->NumKonstS != 0)
|
||||
{
|
||||
printf_wrapper(out, "\nConstant strings:\n");
|
||||
for (i = 0; i < func->NumKonstS; ++i)
|
||||
{
|
||||
printf_wrapper(out, "%3d. %s\n", i, func->KonstS[i].GetChars());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction *func)
|
||||
{
|
||||
VMFunction *callfunc;
|
||||
const char *name;
|
||||
int col;
|
||||
int mode;
|
||||
int a;
|
||||
bool cmp;
|
||||
char cmpname[8];
|
||||
|
||||
for (int i = 0; i < codesize; ++i)
|
||||
{
|
||||
name = OpInfo[code[i].op].Name;
|
||||
mode = OpInfo[code[i].op].Mode;
|
||||
a = code[i].a;
|
||||
cmp = (mode & MODE_ATYPE) == MODE_ACMP;
|
||||
|
||||
// String comparison encodes everything in a single instruction.
|
||||
if (code[i].op == OP_CMPS)
|
||||
{
|
||||
switch (a & CMP_METHOD_MASK)
|
||||
{
|
||||
case CMP_EQ: name = "beq"; break;
|
||||
case CMP_LT: name = "blt"; break;
|
||||
case CMP_LE: name = "ble"; break;
|
||||
}
|
||||
mode = MODE_AIMMZ;
|
||||
mode |= (a & CMP_BK) ? MODE_BKS : MODE_BS;
|
||||
mode |= (a & CMP_CK) ? MODE_CKS : MODE_CS;
|
||||
a &= CMP_CHECK | CMP_APPROX;
|
||||
cmp = true;
|
||||
}
|
||||
if (code[i].op == OP_PARAM && code[i].b & REGT_ADDROF)
|
||||
{
|
||||
name = "parama";
|
||||
}
|
||||
if (cmp)
|
||||
{ // Comparison instruction. Modify name for inverted test.
|
||||
if (!(a & CMP_CHECK))
|
||||
{
|
||||
strcpy(cmpname, name);
|
||||
if (name[1] == 'e')
|
||||
{ // eq -> ne
|
||||
cmpname[1] = 'n', cmpname[2] = 'e';
|
||||
}
|
||||
else if (name[2] == 't')
|
||||
{ // lt -> ge
|
||||
cmpname[1] = 'g', cmpname[2] = 'e';
|
||||
}
|
||||
else
|
||||
{ // le -> gt
|
||||
cmpname[1] = 'g', cmpname[2] = 't';
|
||||
}
|
||||
name = cmpname;
|
||||
}
|
||||
}
|
||||
printf_wrapper(out, "%08x: %02x%02x%02x%02x %-8s", i << 2, code[i].op, code[i].a, code[i].b, code[i].c, name);
|
||||
col = 0;
|
||||
switch (code[i].op)
|
||||
{
|
||||
case OP_JMP:
|
||||
case OP_TRY:
|
||||
col = printf_wrapper(out, "%08x", (i + 1 + code[i].i24) << 2);
|
||||
break;
|
||||
|
||||
case OP_PARAMI:
|
||||
col = printf_wrapper(out, "%d", code[i].i24);
|
||||
break;
|
||||
|
||||
case OP_CALL_K:
|
||||
case OP_TAIL_K:
|
||||
{
|
||||
callfunc = (VMFunction *)func->KonstA[code[i].a].o;
|
||||
col = printf_wrapper(out, "[%p],%d", callfunc, code[i].b);
|
||||
if (code[i].op == OP_CALL_K)
|
||||
{
|
||||
col += printf_wrapper(out, ",%d", code[i].c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_RET:
|
||||
if (code[i].b != REGT_NIL)
|
||||
{
|
||||
if (a == RET_FINAL)
|
||||
{
|
||||
col = print_reg(out, 0, code[i].i16u, MODE_PARAM, 16, func);
|
||||
}
|
||||
else
|
||||
{
|
||||
col = print_reg(out, 0, a & ~RET_FINAL, (mode & MODE_ATYPE) >> MODE_ASHIFT, 24, func);
|
||||
col += print_reg(out, col, code[i].i16u, MODE_PARAM, 16, func);
|
||||
if (a & RET_FINAL)
|
||||
{
|
||||
col += printf_wrapper(out, " [final]");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_RETI:
|
||||
if (a == RET_FINAL)
|
||||
{
|
||||
col = printf_wrapper(out, "%d", code[i].i16);
|
||||
}
|
||||
else
|
||||
{
|
||||
col = print_reg(out, 0, a & ~RET_FINAL, (mode & MODE_ATYPE) >> MODE_ASHIFT, 24, func);
|
||||
col += print_reg(out, col, code[i].i16, MODE_IMMS, 16, func);
|
||||
if (a & RET_FINAL)
|
||||
{
|
||||
col += printf_wrapper(out, " [final]");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_FLOP:
|
||||
col = printf_wrapper(out, "f%d,f%d,%d", code[i].a, code[i].b, code[i].c);
|
||||
if (code[i].c < countof(FlopNames))
|
||||
{
|
||||
col += printf_wrapper(out, " [%s]", FlopNames[code[i].c]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
|
||||
if ((mode & MODE_BCTYPE) == MODE_BCCAST)
|
||||
{
|
||||
switch (code[i].c)
|
||||
{
|
||||
case CASTB_I:
|
||||
mode = MODE_AI | MODE_BI | MODE_CUNUSED;
|
||||
break;
|
||||
case CASTB_A:
|
||||
mode = MODE_AI | MODE_BP | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_I2F:
|
||||
case CAST_U2F:
|
||||
mode = MODE_AF | MODE_BI | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_Co2S:
|
||||
case CAST_So2S:
|
||||
case CAST_N2S:
|
||||
case CAST_I2S:
|
||||
case CAST_U2S:
|
||||
mode = MODE_AS | MODE_BI | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_F2I:
|
||||
case CAST_F2U:
|
||||
case CASTB_F:
|
||||
mode = MODE_AI | MODE_BF | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_F2S:
|
||||
case CAST_V22S:
|
||||
case CAST_V32S:
|
||||
mode = MODE_AS | MODE_BF | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_P2S:
|
||||
mode = MODE_AS | MODE_BP | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_S2Co:
|
||||
case CAST_S2So:
|
||||
case CAST_S2N:
|
||||
case CAST_S2I:
|
||||
case CASTB_S:
|
||||
mode = MODE_AI | MODE_BS | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_S2F:
|
||||
mode = MODE_AF | MODE_BS | MODE_CUNUSED;
|
||||
break;
|
||||
default:
|
||||
mode = MODE_AX | MODE_BX | MODE_CIMMZ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
col = print_reg(out, 0, a, (mode & MODE_ATYPE) >> MODE_ASHIFT, 24, func);
|
||||
if ((mode & MODE_BCTYPE) == MODE_BCTHROW)
|
||||
{
|
||||
if (code[i].a == 0)
|
||||
{
|
||||
mode = (MODE_BP | MODE_CUNUSED);
|
||||
}
|
||||
else if (code[i].a == 1)
|
||||
{
|
||||
mode = (MODE_BKP | MODE_CUNUSED);
|
||||
}
|
||||
else
|
||||
{
|
||||
mode = (MODE_BCJOINT | MODE_BCIMMS);
|
||||
}
|
||||
}
|
||||
else if ((mode & MODE_BCTYPE) == MODE_BCCATCH)
|
||||
{
|
||||
switch (code[i].a)
|
||||
{
|
||||
case 0:
|
||||
mode = MODE_BUNUSED | MODE_CUNUSED;
|
||||
break;
|
||||
case 1:
|
||||
mode = MODE_BUNUSED | MODE_CP;
|
||||
break;
|
||||
case 2:
|
||||
mode = MODE_BP | MODE_CP;
|
||||
break;
|
||||
case 3:
|
||||
mode = MODE_BKP | MODE_CP;
|
||||
break;
|
||||
default:
|
||||
mode = MODE_BIMMZ | MODE_CIMMZ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((mode & (MODE_BTYPE | MODE_CTYPE)) == MODE_BCJOINT)
|
||||
{
|
||||
col += print_reg(out, col, code[i].i16u, (mode & MODE_BCTYPE) >> MODE_BCSHIFT, 16, func);
|
||||
}
|
||||
else
|
||||
{
|
||||
col += print_reg(out, col, code[i].b, (mode & MODE_BTYPE) >> MODE_BSHIFT, 24, func);
|
||||
col += print_reg(out, col, code[i].c, (mode & MODE_CTYPE) >> MODE_CSHIFT, 24, func);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (cmp && i + 1 < codesize)
|
||||
{
|
||||
if (code[i+1].op != OP_JMP)
|
||||
{ // comparison instructions must be followed by jump
|
||||
col += printf_wrapper(out, " => *!*!*!*\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
col += printf_wrapper(out, " => %08x", (i + 2 + code[i+1].i24) << 2);
|
||||
}
|
||||
}
|
||||
if (col > 30)
|
||||
{
|
||||
col = 30;
|
||||
}
|
||||
printf_wrapper(out, "%*c", 30 - col, ';');
|
||||
if (!cmp && (code[i].op == OP_JMP || code[i].op == OP_TRY || code[i].op == OP_PARAMI))
|
||||
{
|
||||
printf_wrapper(out, "%d\n", code[i].i24);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf_wrapper(out, "%d,%d,%d", code[i].a, code[i].b, code[i].c);
|
||||
if (cmp && i + 1 < codesize && code[i+1].op == OP_JMP)
|
||||
{
|
||||
printf_wrapper(out, ",%d\n", code[++i].i24);
|
||||
}
|
||||
else if (code[i].op == OP_CALL_K || code[i].op == OP_TAIL_K)
|
||||
{
|
||||
printf_wrapper(out, " [%s]\n", callfunc->PrintableName.GetChars());
|
||||
}
|
||||
else
|
||||
{
|
||||
printf_wrapper(out, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int print_reg(FILE *out, int col, int arg, int mode, int immshift, const VMScriptFunction *func)
|
||||
{
|
||||
if (mode == MODE_UNUSED || mode == MODE_CMP)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (col > 0)
|
||||
{
|
||||
col = printf_wrapper(out, ",");
|
||||
}
|
||||
switch(mode)
|
||||
{
|
||||
case MODE_I:
|
||||
return col+printf_wrapper(out, "d%d", arg);
|
||||
case MODE_F:
|
||||
return col+printf_wrapper(out, "f%d", arg);
|
||||
case MODE_S:
|
||||
return col+printf_wrapper(out, "s%d", arg);
|
||||
case MODE_P:
|
||||
return col+printf_wrapper(out, "a%d", arg);
|
||||
case MODE_V:
|
||||
return col+printf_wrapper(out, "v%d", arg);
|
||||
|
||||
case MODE_KI:
|
||||
if (func != NULL)
|
||||
{
|
||||
return col+printf_wrapper(out, "%d", func->KonstD[arg]);
|
||||
}
|
||||
return printf_wrapper(out, "kd%d", arg);
|
||||
case MODE_KF:
|
||||
if (func != NULL)
|
||||
{
|
||||
return col+printf_wrapper(out, "%#g", func->KonstF[arg]);
|
||||
}
|
||||
return col+printf_wrapper(out, "kf%d", arg);
|
||||
case MODE_KS:
|
||||
if (func != NULL)
|
||||
{
|
||||
return col+printf_wrapper(out, "\"%.27s\"", func->KonstS[arg].GetChars());
|
||||
}
|
||||
return col+printf_wrapper(out, "ks%d", arg);
|
||||
case MODE_KP:
|
||||
if (func != NULL)
|
||||
{
|
||||
return col+printf_wrapper(out, "%p", func->KonstA[arg]);
|
||||
}
|
||||
return col+printf_wrapper(out, "ka%d", arg);
|
||||
case MODE_KV:
|
||||
if (func != NULL)
|
||||
{
|
||||
return col+printf_wrapper(out, "(%f,%f,%f)", func->KonstF[arg], func->KonstF[arg+1], func->KonstF[arg+2]);
|
||||
}
|
||||
return col+printf_wrapper(out, "kv%d", arg);
|
||||
|
||||
case MODE_IMMS:
|
||||
return col+printf_wrapper(out, "%d", (arg << immshift) >> immshift);
|
||||
|
||||
case MODE_IMMZ:
|
||||
return col+printf_wrapper(out, "%d", arg);
|
||||
|
||||
case MODE_PARAM:
|
||||
{
|
||||
int regtype, regnum;
|
||||
#ifdef __BIG_ENDIAN__
|
||||
regtype = (arg >> 8) & 255;
|
||||
regnum = arg & 255;
|
||||
#else
|
||||
regtype = arg & 255;
|
||||
regnum = (arg >> 8) & 255;
|
||||
#endif
|
||||
switch (regtype & (REGT_TYPE | REGT_KONST | REGT_MULTIREG))
|
||||
{
|
||||
case REGT_INT:
|
||||
return col+printf_wrapper(out, "d%d", regnum);
|
||||
case REGT_FLOAT:
|
||||
return col+printf_wrapper(out, "f%d", regnum);
|
||||
case REGT_STRING:
|
||||
return col+printf_wrapper(out, "s%d", regnum);
|
||||
case REGT_POINTER:
|
||||
return col+printf_wrapper(out, "a%d", regnum);
|
||||
case REGT_FLOAT | REGT_MULTIREG2:
|
||||
return col+printf_wrapper(out, "v%d.2", regnum);
|
||||
case REGT_FLOAT | REGT_MULTIREG3:
|
||||
return col+printf_wrapper(out, "v%d.3", regnum);
|
||||
case REGT_INT | REGT_KONST:
|
||||
return col+print_reg(out, 0, regnum, MODE_KI, 0, func);
|
||||
case REGT_FLOAT | REGT_KONST:
|
||||
return col+print_reg(out, 0, regnum, MODE_KF, 0, func);
|
||||
case REGT_STRING | REGT_KONST:
|
||||
return col+print_reg(out, 0, regnum, MODE_KS, 0, func);
|
||||
case REGT_POINTER | REGT_KONST:
|
||||
return col+print_reg(out, 0, regnum, MODE_KP, 0, func);
|
||||
case REGT_FLOAT | REGT_MULTIREG | REGT_KONST:
|
||||
return col+print_reg(out, 0, regnum, MODE_KV, 0, func);
|
||||
default:
|
||||
if (regtype == REGT_NIL)
|
||||
{
|
||||
return col+printf_wrapper(out, "nil");
|
||||
}
|
||||
return col+printf_wrapper(out, "param[t=%d,%c,%c,n=%d]",
|
||||
regtype & REGT_TYPE,
|
||||
regtype & REGT_KONST ? 'k' : 'r',
|
||||
regtype & REGT_MULTIREG ? 'm' : 's',
|
||||
regnum);
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return col+printf_wrapper(out, "$%d", arg);
|
||||
}
|
||||
return col;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Do some postprocessing after everything has been defined
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void DumpFunction(FILE *dump, VMScriptFunction *sfunc, const char *label, int labellen)
|
||||
{
|
||||
const char *marks = "=======================================================";
|
||||
fprintf(dump, "\n%.*s %s %.*s", MAX(3, 38 - labellen / 2), marks, label, MAX(3, 38 - labellen / 2), marks);
|
||||
fprintf(dump, "\nInteger regs: %-3d Float regs: %-3d Address regs: %-3d String regs: %-3d\nStack size: %d\n",
|
||||
sfunc->NumRegD, sfunc->NumRegF, sfunc->NumRegA, sfunc->NumRegS, sfunc->MaxParam);
|
||||
VMDumpConstants(dump, sfunc);
|
||||
fprintf(dump, "\nDisassembly @ %p:\n", sfunc->Code);
|
||||
VMDisasm(dump, sfunc->Code, sfunc->CodeSize, sfunc);
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue