mirror of
https://github.com/ZDoom/Raze.git
synced 2025-05-31 01:11:15 +00:00
- Backend update from GZDoom
* Vector 4 in zscript * garbage collector fixes
This commit is contained in:
parent
f7a2fd29ba
commit
8806fb930b
32 changed files with 1294 additions and 328 deletions
|
@ -124,8 +124,10 @@ xx(State)
|
||||||
xx(Fixed)
|
xx(Fixed)
|
||||||
xx(Vector2)
|
xx(Vector2)
|
||||||
xx(Vector3)
|
xx(Vector3)
|
||||||
|
xx(Vector4)
|
||||||
xx(FVector2)
|
xx(FVector2)
|
||||||
xx(FVector3)
|
xx(FVector3)
|
||||||
|
xx(FVector4)
|
||||||
xx(let)
|
xx(let)
|
||||||
|
|
||||||
xx(Min)
|
xx(Min)
|
||||||
|
@ -175,7 +177,9 @@ xx(b)
|
||||||
xx(X)
|
xx(X)
|
||||||
xx(Y)
|
xx(Y)
|
||||||
xx(Z)
|
xx(Z)
|
||||||
|
xx(W)
|
||||||
xx(XY)
|
xx(XY)
|
||||||
|
xx(XYZ)
|
||||||
|
|
||||||
xx(Prototype)
|
xx(Prototype)
|
||||||
xx(Void)
|
xx(Void)
|
||||||
|
|
|
@ -326,6 +326,11 @@ inline FSerializer &Serialize(FSerializer &arc, const char *key, DVector2 &p, DV
|
||||||
return arc.Array<double>(key, &p[0], def? &(*def)[0] : nullptr, 2, true);
|
return arc.Array<double>(key, &p[0], def? &(*def)[0] : nullptr, 2, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline FSerializer& Serialize(FSerializer& arc, const char* key, FVector4& p, FVector4* def)
|
||||||
|
{
|
||||||
|
return arc.Array<float>(key, &p[0], def ? &(*def)[0] : nullptr, 4, true);
|
||||||
|
}
|
||||||
|
|
||||||
inline FSerializer& Serialize(FSerializer& arc, const char* key, FVector3& p, FVector3* def)
|
inline FSerializer& Serialize(FSerializer& arc, const char* key, FVector3& p, FVector3* def)
|
||||||
{
|
{
|
||||||
return arc.Array<float>(key, &p[0], def ? &(*def)[0] : nullptr, 3, true);
|
return arc.Array<float>(key, &p[0], def ? &(*def)[0] : nullptr, 3, true);
|
||||||
|
|
|
@ -48,6 +48,7 @@ public:
|
||||||
cycle_t &operator= (const cycle_t &o) { return *this; }
|
cycle_t &operator= (const cycle_t &o) { return *this; }
|
||||||
void Reset() {}
|
void Reset() {}
|
||||||
void Clock() {}
|
void Clock() {}
|
||||||
|
void ResetAndClock() {}
|
||||||
void Unclock() {}
|
void Unclock() {}
|
||||||
double Time() { return 0; }
|
double Time() { return 0; }
|
||||||
double TimeMS() { return 0; }
|
double TimeMS() { return 0; }
|
||||||
|
@ -120,6 +121,12 @@ public:
|
||||||
Sec -= ts.tv_sec + ts.tv_nsec * 1e-9;
|
Sec -= ts.tv_sec + ts.tv_nsec * 1e-9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResetAndClock()
|
||||||
|
{
|
||||||
|
Reset();
|
||||||
|
Clock();
|
||||||
|
}
|
||||||
|
|
||||||
void Unclock()
|
void Unclock()
|
||||||
{
|
{
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
|
@ -221,6 +228,11 @@ public:
|
||||||
Counter = 0;
|
Counter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResetAndClock()
|
||||||
|
{
|
||||||
|
Counter = -static_cast<int64_t>(rdtsc());
|
||||||
|
}
|
||||||
|
|
||||||
void Clock()
|
void Clock()
|
||||||
{
|
{
|
||||||
int64_t time = rdtsc();
|
int64_t time = rdtsc();
|
||||||
|
|
|
@ -250,6 +250,12 @@ static bool CheckSkipOptionBlock(FScanner &sc)
|
||||||
filter = true;
|
filter = true;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
else if (sc.Compare("SWRender"))
|
||||||
|
{
|
||||||
|
#ifndef NO_SWRENDERER
|
||||||
|
filter = true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
while (sc.CheckString(","));
|
while (sc.CheckString(","));
|
||||||
sc.MustGetStringName(")");
|
sc.MustGetStringName(")");
|
||||||
|
|
|
@ -82,23 +82,58 @@
|
||||||
** infinity, where each step performs a full collection.) You can also
|
** infinity, where each step performs a full collection.) You can also
|
||||||
** change this value dynamically.
|
** change this value dynamically.
|
||||||
*/
|
*/
|
||||||
#define DEFAULT_GCMUL 200 // GC runs 'double the speed' of memory allocation
|
#ifndef _DEBUG
|
||||||
|
#define DEFAULT_GCMUL 600 // GC runs gcmul% the speed of memory allocation
|
||||||
|
#else
|
||||||
|
// Higher in debug builds to account for the extra time spent freeing objects
|
||||||
|
#define DEFAULT_GCMUL 800
|
||||||
|
#endif
|
||||||
|
|
||||||
// Minimum step size
|
// Minimum step size
|
||||||
#define GCSTEPSIZE (sizeof(DObject) * 16)
|
#define GCMINSTEPSIZE (sizeof(DObject) * 16)
|
||||||
|
|
||||||
// Maximum number of elements to sweep in a single step
|
// Sweeps traverse objects in chunks of this size
|
||||||
#define GCSWEEPMAX 40
|
#define GCSWEEPGRANULARITY 40
|
||||||
|
|
||||||
// Cost of sweeping one element (the size of a small object divided by
|
// Cost of deleting an object
|
||||||
// some adjust for the sweep speed)
|
#ifndef _DEBUG
|
||||||
#define GCSWEEPCOST (sizeof(DObject) / 4)
|
#define GCDELETECOST 75
|
||||||
|
#else
|
||||||
|
// Freeing memory is much more costly in debug builds
|
||||||
|
#define GCDELETECOST 230
|
||||||
|
#endif
|
||||||
|
|
||||||
// Cost of calling of one destructor
|
// Cost of destroying an object
|
||||||
#define GCFINALIZECOST 100
|
#define GCDESTROYCOST 15
|
||||||
|
|
||||||
// TYPES -------------------------------------------------------------------
|
// TYPES -------------------------------------------------------------------
|
||||||
|
|
||||||
|
class FAveragizer
|
||||||
|
{
|
||||||
|
// Number of allocations to track
|
||||||
|
static inline constexpr unsigned HistorySize = 512;
|
||||||
|
|
||||||
|
size_t History[HistorySize];
|
||||||
|
size_t TotalAmount;
|
||||||
|
int TotalCount;
|
||||||
|
unsigned NewestPos;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FAveragizer();
|
||||||
|
void AddAlloc(size_t alloc);
|
||||||
|
size_t GetAverage();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FStepStats
|
||||||
|
{
|
||||||
|
cycle_t Clock[GC::GCS_COUNT];
|
||||||
|
size_t BytesCovered[GC::GCS_COUNT];
|
||||||
|
int Count[GC::GCS_COUNT];
|
||||||
|
|
||||||
|
void Format(FString &out);
|
||||||
|
void Reset();
|
||||||
|
};
|
||||||
|
|
||||||
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
||||||
|
|
||||||
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
||||||
|
@ -114,28 +149,50 @@ static size_t CalcStepSize();
|
||||||
namespace GC
|
namespace GC
|
||||||
{
|
{
|
||||||
size_t AllocBytes;
|
size_t AllocBytes;
|
||||||
|
size_t RunningAllocBytes;
|
||||||
|
size_t RunningDeallocBytes;
|
||||||
size_t Threshold;
|
size_t Threshold;
|
||||||
size_t Estimate;
|
size_t Estimate;
|
||||||
DObject *Gray;
|
DObject *Gray;
|
||||||
DObject *Root;
|
DObject *Root;
|
||||||
DObject *SoftRoots;
|
DObject *SoftRoots;
|
||||||
DObject **SweepPos;
|
DObject **SweepPos;
|
||||||
|
DObject *ToDestroy;
|
||||||
uint32_t CurrentWhite = OF_White0 | OF_Fixed;
|
uint32_t CurrentWhite = OF_White0 | OF_Fixed;
|
||||||
EGCState State = GCS_Pause;
|
EGCState State = GCS_Pause;
|
||||||
int Pause = DEFAULT_GCPAUSE;
|
int Pause = DEFAULT_GCPAUSE;
|
||||||
int StepMul = DEFAULT_GCMUL;
|
int StepMul = DEFAULT_GCMUL;
|
||||||
int StepCount;
|
FStepStats StepStats;
|
||||||
uint64_t CheckTime;
|
FStepStats PrevStepStats;
|
||||||
bool FinalGC;
|
bool FinalGC;
|
||||||
|
bool HadToDestroy;
|
||||||
|
|
||||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||||
|
|
||||||
static int LastCollectTime; // Time last time collector finished
|
static FAveragizer AllocHistory;// Tracks allocation rate over time
|
||||||
static size_t LastCollectAlloc; // Memory allocation when collector finished
|
static cycle_t GCTime; // Track time spent in GC
|
||||||
static size_t MinStepSize; // Cover at least this much memory per step
|
|
||||||
|
|
||||||
// CODE --------------------------------------------------------------------
|
// CODE --------------------------------------------------------------------
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// CheckGC
|
||||||
|
//
|
||||||
|
// Check if it's time to collect, and do a collection step if it is.
|
||||||
|
// Also does some bookkeeping. Should be called fairly consistantly.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void CheckGC()
|
||||||
|
{
|
||||||
|
AllocHistory.AddAlloc(RunningAllocBytes);
|
||||||
|
RunningAllocBytes = 0;
|
||||||
|
if (State > GCS_Pause || AllocBytes >= Threshold)
|
||||||
|
{
|
||||||
|
Step();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// SetThreshold
|
// SetThreshold
|
||||||
|
@ -146,7 +203,7 @@ static size_t MinStepSize; // Cover at least this much memory per step
|
||||||
|
|
||||||
void SetThreshold()
|
void SetThreshold()
|
||||||
{
|
{
|
||||||
Threshold = (Estimate / 100) * Pause;
|
Threshold = (std::min(Estimate, AllocBytes) / 100) * Pause;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -170,55 +227,72 @@ size_t PropagateMark()
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// SweepList
|
// SweepObjects
|
||||||
//
|
//
|
||||||
// Runs a limited sweep on a list, returning the position in the list just
|
// Runs a limited sweep on the object list, returning the number of bytes
|
||||||
// after the last object swept.
|
// swept.
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
static DObject **SweepList(DObject **p, size_t count, size_t *finalize_count)
|
static size_t SweepObjects(size_t count)
|
||||||
{
|
{
|
||||||
DObject *curr;
|
DObject *curr;
|
||||||
int deadmask = OtherWhite();
|
int deadmask = OtherWhite();
|
||||||
size_t finalized = 0;
|
size_t swept = 0;
|
||||||
|
|
||||||
while ((curr = *p) != NULL && count-- > 0)
|
while ((curr = *SweepPos) != nullptr && count-- > 0)
|
||||||
{
|
{
|
||||||
|
swept += curr->GetClass()->Size;
|
||||||
if ((curr->ObjectFlags ^ OF_WhiteBits) & deadmask) // not dead?
|
if ((curr->ObjectFlags ^ OF_WhiteBits) & deadmask) // not dead?
|
||||||
{
|
{
|
||||||
assert(!curr->IsDead() || (curr->ObjectFlags & OF_Fixed));
|
assert(!curr->IsDead() || (curr->ObjectFlags & OF_Fixed));
|
||||||
curr->MakeWhite(); // make it white (for next cycle)
|
curr->MakeWhite(); // make it white (for next cycle)
|
||||||
p = &curr->ObjNext;
|
SweepPos = &curr->ObjNext;
|
||||||
}
|
}
|
||||||
else // must erase 'curr'
|
else
|
||||||
{
|
{
|
||||||
assert(curr->IsDead());
|
assert(curr->IsDead());
|
||||||
*p = curr->ObjNext;
|
|
||||||
if (!(curr->ObjectFlags & OF_EuthanizeMe))
|
if (!(curr->ObjectFlags & OF_EuthanizeMe))
|
||||||
{ // The object must be destroyed before it can be finalized.
|
{ // The object must be destroyed before it can be deleted.
|
||||||
// Note that thinkers must already have been destroyed. If they get here without
|
curr->GCNext = ToDestroy;
|
||||||
// having been destroyed first, it means they somehow became unattached from the
|
ToDestroy = curr;
|
||||||
// thinker lists. If I don't maintain the invariant that all live thinkers must
|
SweepPos = &curr->ObjNext;
|
||||||
// be in a thinker list, then I need to add write barriers for every time a
|
}
|
||||||
// thinker pointer is changed. This seems easier and perfectly reasonable, since
|
else
|
||||||
// a live thinker that isn't on a thinker list isn't much of a thinker.
|
{ // must erase 'curr'
|
||||||
|
*SweepPos = curr->ObjNext;
|
||||||
// However, this can happen during deletion of the thinker list while cleaning up
|
curr->ObjectFlags |= OF_Cleanup;
|
||||||
// from a savegame error so we can't assume that any thinker that gets here is an error.
|
delete curr;
|
||||||
|
swept += GCDELETECOST;
|
||||||
curr->Destroy();
|
|
||||||
}
|
}
|
||||||
curr->ObjectFlags |= OF_Cleanup;
|
|
||||||
delete curr;
|
|
||||||
finalized++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (finalize_count != NULL)
|
return swept;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// DestroyObjects
|
||||||
|
//
|
||||||
|
// Destroys up to count objects on a list linked on GCNext, returning the
|
||||||
|
// size of objects destroyed, for updating the estimate.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
static size_t DestroyObjects(size_t count)
|
||||||
|
{
|
||||||
|
DObject *curr;
|
||||||
|
size_t bytes_destroyed = 0;
|
||||||
|
|
||||||
|
while ((curr = ToDestroy) != nullptr && count-- > 0)
|
||||||
{
|
{
|
||||||
*finalize_count = finalized;
|
assert(!(curr->ObjectFlags & OF_EuthanizeMe));
|
||||||
|
bytes_destroyed += curr->GetClass()->Size + GCDESTROYCOST;
|
||||||
|
ToDestroy = curr->GCNext;
|
||||||
|
curr->GCNext = nullptr;
|
||||||
|
curr->Destroy();
|
||||||
}
|
}
|
||||||
return p;
|
return bytes_destroyed;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -269,20 +343,14 @@ void MarkArray(DObject **obj, size_t count)
|
||||||
//
|
//
|
||||||
// CalcStepSize
|
// CalcStepSize
|
||||||
//
|
//
|
||||||
// Decide how big a step should be based, depending on how long it took to
|
// Decide how big a step should be, based on the current allocation rate.
|
||||||
// allocate up to the threshold from the amount left after the previous
|
|
||||||
// collection.
|
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
static size_t CalcStepSize()
|
static size_t CalcStepSize()
|
||||||
{
|
{
|
||||||
int time_passed = int(CheckTime - LastCollectTime);
|
size_t avg = AllocHistory.GetAverage();
|
||||||
auto alloc = min(LastCollectAlloc, Estimate);
|
return std::max<size_t>(GCMINSTEPSIZE, avg * StepMul / 100);
|
||||||
size_t bytes_gained = AllocBytes > alloc ? AllocBytes - alloc : 0;
|
|
||||||
return (StepMul > 0 && time_passed > 0)
|
|
||||||
? std::max<size_t>(GCSTEPSIZE, bytes_gained / time_passed * StepMul / 100)
|
|
||||||
: std::numeric_limits<size_t>::max() / 2; // no limit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -302,15 +370,18 @@ void AddMarkerFunc(GCMarkerFunc func)
|
||||||
|
|
||||||
static void MarkRoot()
|
static void MarkRoot()
|
||||||
{
|
{
|
||||||
Gray = NULL;
|
PrevStepStats = StepStats;
|
||||||
|
StepStats.Reset();
|
||||||
|
|
||||||
|
Gray = nullptr;
|
||||||
|
|
||||||
for (auto func : markers) func();
|
for (auto func : markers) func();
|
||||||
|
|
||||||
// Mark soft roots.
|
// Mark soft roots.
|
||||||
if (SoftRoots != NULL)
|
if (SoftRoots != nullptr)
|
||||||
{
|
{
|
||||||
DObject **probe = &SoftRoots->ObjNext;
|
DObject **probe = &SoftRoots->ObjNext;
|
||||||
while (*probe != NULL)
|
while (*probe != nullptr)
|
||||||
{
|
{
|
||||||
DObject *soft = *probe;
|
DObject *soft = *probe;
|
||||||
probe = &soft->ObjNext;
|
probe = &soft->ObjNext;
|
||||||
|
@ -322,7 +393,6 @@ static void MarkRoot()
|
||||||
}
|
}
|
||||||
// Time to propagate the marks.
|
// Time to propagate the marks.
|
||||||
State = GCS_Propagate;
|
State = GCS_Propagate;
|
||||||
StepCount = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -341,10 +411,21 @@ static void Atomic()
|
||||||
SweepPos = &Root;
|
SweepPos = &Root;
|
||||||
State = GCS_Sweep;
|
State = GCS_Sweep;
|
||||||
Estimate = AllocBytes;
|
Estimate = AllocBytes;
|
||||||
|
}
|
||||||
|
|
||||||
// Now that we are about to start a sweep, establish a baseline minimum
|
//==========================================================================
|
||||||
// step size for how much memory we want to sweep each CheckGC().
|
//
|
||||||
MinStepSize = CalcStepSize();
|
// SweepDone
|
||||||
|
//
|
||||||
|
// Sets up the Destroy phase, if there are any dead objects that haven't
|
||||||
|
// been destroyed yet, or skips to the Done state.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
static void SweepDone()
|
||||||
|
{
|
||||||
|
HadToDestroy = ToDestroy != nullptr;
|
||||||
|
State = HadToDestroy ? GCS_Destroy : GCS_Done;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -364,7 +445,7 @@ static size_t SingleStep()
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case GCS_Propagate:
|
case GCS_Propagate:
|
||||||
if (Gray != NULL)
|
if (Gray != nullptr)
|
||||||
{
|
{
|
||||||
return PropagateMark();
|
return PropagateMark();
|
||||||
}
|
}
|
||||||
|
@ -375,22 +456,30 @@ static size_t SingleStep()
|
||||||
}
|
}
|
||||||
|
|
||||||
case GCS_Sweep: {
|
case GCS_Sweep: {
|
||||||
size_t old = AllocBytes;
|
RunningDeallocBytes = 0;
|
||||||
size_t finalize_count;
|
size_t swept = SweepObjects(GCSWEEPGRANULARITY);
|
||||||
SweepPos = SweepList(SweepPos, GCSWEEPMAX, &finalize_count);
|
Estimate -= RunningDeallocBytes;
|
||||||
if (*SweepPos == NULL)
|
if (*SweepPos == nullptr)
|
||||||
{ // Nothing more to sweep?
|
{ // Nothing more to sweep?
|
||||||
State = GCS_Finalize;
|
SweepDone();
|
||||||
}
|
}
|
||||||
//assert(old >= AllocBytes);
|
return swept;
|
||||||
Estimate -= max<size_t>(0, old - AllocBytes);
|
|
||||||
return (GCSWEEPMAX - finalize_count) * GCSWEEPCOST + finalize_count * GCFINALIZECOST;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case GCS_Finalize:
|
case GCS_Destroy: {
|
||||||
|
size_t destroy_size;
|
||||||
|
destroy_size = DestroyObjects(GCSWEEPGRANULARITY);
|
||||||
|
Estimate -= destroy_size;
|
||||||
|
if (ToDestroy == nullptr)
|
||||||
|
{ // Nothing more to destroy?
|
||||||
|
State = GCS_Done;
|
||||||
|
}
|
||||||
|
return destroy_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
case GCS_Done:
|
||||||
State = GCS_Pause; // end collection
|
State = GCS_Pause; // end collection
|
||||||
LastCollectAlloc = AllocBytes;
|
SetThreshold();
|
||||||
LastCollectTime = (int)CheckTime;
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -403,21 +492,27 @@ static size_t SingleStep()
|
||||||
//
|
//
|
||||||
// Step
|
// Step
|
||||||
//
|
//
|
||||||
// Performs enough single steps to cover GCSTEPSIZE * StepMul% bytes of
|
// Performs enough single steps to cover <StepSize> bytes of memory.
|
||||||
// memory.
|
// Some of those bytes might be "fake" to account for the cost of freeing
|
||||||
|
// or destroying object.
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
void Step()
|
void Step()
|
||||||
{
|
{
|
||||||
// We recalculate a step size in case the rate of allocation went up
|
GCTime.ResetAndClock();
|
||||||
// since we started sweeping because we don't want to fall behind.
|
|
||||||
// However, we also don't want to go slower than what was decided upon
|
auto enter_state = State;
|
||||||
// when the sweep began if the rate of allocation has slowed.
|
StepStats.Count[enter_state]++;
|
||||||
size_t lim = max(CalcStepSize(), MinStepSize);
|
StepStats.Clock[enter_state].Clock();
|
||||||
|
|
||||||
|
size_t did = 0;
|
||||||
|
size_t lim = CalcStepSize();
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
size_t done = SingleStep();
|
size_t done = SingleStep();
|
||||||
|
did += done;
|
||||||
if (done < lim)
|
if (done < lim)
|
||||||
{
|
{
|
||||||
lim -= done;
|
lim -= done;
|
||||||
|
@ -426,17 +521,23 @@ void Step()
|
||||||
{
|
{
|
||||||
lim = 0;
|
lim = 0;
|
||||||
}
|
}
|
||||||
|
if (State != enter_state)
|
||||||
|
{
|
||||||
|
// Finish stats on old state
|
||||||
|
StepStats.Clock[enter_state].Unclock();
|
||||||
|
StepStats.BytesCovered[enter_state] += did;
|
||||||
|
|
||||||
|
// Start stats on new state
|
||||||
|
did = 0;
|
||||||
|
enter_state = State;
|
||||||
|
StepStats.Clock[enter_state].Clock();
|
||||||
|
StepStats.Count[enter_state]++;
|
||||||
|
}
|
||||||
} while (lim && State != GCS_Pause);
|
} while (lim && State != GCS_Pause);
|
||||||
if (State != GCS_Pause)
|
|
||||||
{
|
StepStats.Clock[enter_state].Unclock();
|
||||||
Threshold = AllocBytes;
|
StepStats.BytesCovered[enter_state] += did;
|
||||||
}
|
GCTime.Unclock();
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(AllocBytes >= Estimate);
|
|
||||||
SetThreshold();
|
|
||||||
}
|
|
||||||
StepCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -454,20 +555,23 @@ void FullGC()
|
||||||
// Reset sweep mark to sweep all elements (returning them to white)
|
// Reset sweep mark to sweep all elements (returning them to white)
|
||||||
SweepPos = &Root;
|
SweepPos = &Root;
|
||||||
// Reset other collector lists
|
// Reset other collector lists
|
||||||
Gray = NULL;
|
Gray = nullptr;
|
||||||
State = GCS_Sweep;
|
State = GCS_Sweep;
|
||||||
}
|
}
|
||||||
// Finish any pending sweep phase
|
// Finish any pending GC stages
|
||||||
while (State != GCS_Finalize)
|
|
||||||
{
|
|
||||||
SingleStep();
|
|
||||||
}
|
|
||||||
MarkRoot();
|
|
||||||
while (State != GCS_Pause)
|
while (State != GCS_Pause)
|
||||||
{
|
{
|
||||||
SingleStep();
|
SingleStep();
|
||||||
}
|
}
|
||||||
SetThreshold();
|
// Loop until everything that can be destroyed and freed is
|
||||||
|
do
|
||||||
|
{
|
||||||
|
MarkRoot();
|
||||||
|
while (State != GCS_Pause)
|
||||||
|
{
|
||||||
|
SingleStep();
|
||||||
|
}
|
||||||
|
} while (HadToDestroy);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -481,9 +585,9 @@ void FullGC()
|
||||||
|
|
||||||
void Barrier(DObject *pointing, DObject *pointed)
|
void Barrier(DObject *pointing, DObject *pointed)
|
||||||
{
|
{
|
||||||
assert(pointing == NULL || (pointing->IsBlack() && !pointing->IsDead()));
|
assert(pointing == nullptr || (pointing->IsBlack() && !pointing->IsDead()));
|
||||||
assert(pointed->IsWhite() && !pointed->IsDead());
|
assert(pointed->IsWhite() && !pointed->IsDead());
|
||||||
assert(State != GCS_Finalize && State != GCS_Pause);
|
assert(State != GCS_Destroy && State != GCS_Pause);
|
||||||
assert(!(pointed->ObjectFlags & OF_Released)); // if a released object gets here, something must be wrong.
|
assert(!(pointed->ObjectFlags & OF_Released)); // if a released object gets here, something must be wrong.
|
||||||
if (pointed->ObjectFlags & OF_Released) return; // don't do anything with non-GC'd objects.
|
if (pointed->ObjectFlags & OF_Released) return; // don't do anything with non-GC'd objects.
|
||||||
// The invariant only needs to be maintained in the propagate state.
|
// The invariant only needs to be maintained in the propagate state.
|
||||||
|
@ -495,7 +599,7 @@ void Barrier(DObject *pointing, DObject *pointed)
|
||||||
}
|
}
|
||||||
// In other states, we can mark the pointing object white so this
|
// In other states, we can mark the pointing object white so this
|
||||||
// barrier won't be triggered again, saving a few cycles in the future.
|
// barrier won't be triggered again, saving a few cycles in the future.
|
||||||
else if (pointing != NULL)
|
else if (pointing != nullptr)
|
||||||
{
|
{
|
||||||
pointing->MakeWhite();
|
pointing->MakeWhite();
|
||||||
}
|
}
|
||||||
|
@ -503,13 +607,13 @@ void Barrier(DObject *pointing, DObject *pointed)
|
||||||
|
|
||||||
void DelSoftRootHead()
|
void DelSoftRootHead()
|
||||||
{
|
{
|
||||||
if (SoftRoots != NULL)
|
if (SoftRoots != nullptr)
|
||||||
{
|
{
|
||||||
// Don't let the destructor print a warning message
|
// Don't let the destructor print a warning message
|
||||||
SoftRoots->ObjectFlags |= OF_YesReallyDelete;
|
SoftRoots->ObjectFlags |= OF_YesReallyDelete;
|
||||||
delete SoftRoots;
|
delete SoftRoots;
|
||||||
}
|
}
|
||||||
SoftRoots = NULL;
|
SoftRoots = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -526,7 +630,7 @@ void AddSoftRoot(DObject *obj)
|
||||||
DObject **probe;
|
DObject **probe;
|
||||||
|
|
||||||
// Are there any soft roots yet?
|
// Are there any soft roots yet?
|
||||||
if (SoftRoots == NULL)
|
if (SoftRoots == nullptr)
|
||||||
{
|
{
|
||||||
// Create a new object to root the soft roots off of, and stick
|
// Create a new object to root the soft roots off of, and stick
|
||||||
// it at the end of the object list, so we know that anything
|
// it at the end of the object list, so we know that anything
|
||||||
|
@ -534,17 +638,17 @@ void AddSoftRoot(DObject *obj)
|
||||||
SoftRoots = Create<DObject>();
|
SoftRoots = Create<DObject>();
|
||||||
SoftRoots->ObjectFlags |= OF_Fixed;
|
SoftRoots->ObjectFlags |= OF_Fixed;
|
||||||
probe = &Root;
|
probe = &Root;
|
||||||
while (*probe != NULL)
|
while (*probe != nullptr)
|
||||||
{
|
{
|
||||||
probe = &(*probe)->ObjNext;
|
probe = &(*probe)->ObjNext;
|
||||||
}
|
}
|
||||||
Root = SoftRoots->ObjNext;
|
Root = SoftRoots->ObjNext;
|
||||||
SoftRoots->ObjNext = NULL;
|
SoftRoots->ObjNext = nullptr;
|
||||||
*probe = SoftRoots;
|
*probe = SoftRoots;
|
||||||
}
|
}
|
||||||
// Mark this object as rooted and move it after the SoftRoots marker.
|
// Mark this object as rooted and move it after the SoftRoots marker.
|
||||||
probe = &Root;
|
probe = &Root;
|
||||||
while (*probe != NULL && *probe != obj)
|
while (*probe != nullptr && *probe != obj)
|
||||||
{
|
{
|
||||||
probe = &(*probe)->ObjNext;
|
probe = &(*probe)->ObjNext;
|
||||||
}
|
}
|
||||||
|
@ -567,14 +671,14 @@ void DelSoftRoot(DObject *obj)
|
||||||
{
|
{
|
||||||
DObject **probe;
|
DObject **probe;
|
||||||
|
|
||||||
if (!(obj->ObjectFlags & OF_Rooted))
|
if (obj == nullptr || !(obj->ObjectFlags & OF_Rooted))
|
||||||
{ // Not rooted, so nothing to do.
|
{ // Not rooted, so nothing to do.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
obj->ObjectFlags &= ~OF_Rooted;
|
obj->ObjectFlags &= ~OF_Rooted;
|
||||||
// Move object out of the soft roots part of the list.
|
// Move object out of the soft roots part of the list.
|
||||||
probe = &SoftRoots;
|
probe = &SoftRoots;
|
||||||
while (*probe != NULL && *probe != obj)
|
while (*probe != nullptr && *probe != obj)
|
||||||
{
|
{
|
||||||
probe = &(*probe)->ObjNext;
|
probe = &(*probe)->ObjNext;
|
||||||
}
|
}
|
||||||
|
@ -588,6 +692,52 @@ void DelSoftRoot(DObject *obj)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FAveragizer - Constructor
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FAveragizer::FAveragizer()
|
||||||
|
{
|
||||||
|
NewestPos = 0;
|
||||||
|
TotalAmount = 0;
|
||||||
|
TotalCount = 0;
|
||||||
|
memset(History, 0, sizeof(History));
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FAveragizer :: AddAlloc
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FAveragizer::AddAlloc(size_t alloc)
|
||||||
|
{
|
||||||
|
NewestPos = (NewestPos + 1) & (HistorySize - 1);
|
||||||
|
if (TotalCount < HistorySize)
|
||||||
|
{
|
||||||
|
TotalCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TotalAmount -= History[NewestPos];
|
||||||
|
}
|
||||||
|
History[NewestPos] = alloc;
|
||||||
|
TotalAmount += alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FAveragizer :: GetAverage
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
size_t FAveragizer::GetAverage()
|
||||||
|
{
|
||||||
|
return TotalCount != 0 ? TotalAmount / TotalCount : 0;
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// STAT gc
|
// STAT gc
|
||||||
|
@ -602,18 +752,66 @@ ADD_STAT(gc)
|
||||||
" Pause ",
|
" Pause ",
|
||||||
"Propagate",
|
"Propagate",
|
||||||
" Sweep ",
|
" Sweep ",
|
||||||
"Finalize " };
|
" Destroy ",
|
||||||
|
" Done "
|
||||||
|
};
|
||||||
FString out;
|
FString out;
|
||||||
out.Format("[%s] Alloc:%6zuK Thresh:%6zuK Est:%6zuK Steps: %d %zuK",
|
double time = GC::State != GC::GCS_Pause ? GC::GCTime.TimeMS() : 0;
|
||||||
|
|
||||||
|
GC::PrevStepStats.Format(out);
|
||||||
|
out << "\n";
|
||||||
|
GC::StepStats.Format(out);
|
||||||
|
out.AppendFormat("\n%.2fms [%s] Rate:%3zuK (%3zuK) Alloc:%6zuK Est:%6zuK Thresh:%6zuK",
|
||||||
|
time,
|
||||||
StateStrings[GC::State],
|
StateStrings[GC::State],
|
||||||
|
(GC::AllocHistory.GetAverage() + 1023) >> 10,
|
||||||
|
(GC::CalcStepSize() + 1023) >> 10,
|
||||||
(GC::AllocBytes + 1023) >> 10,
|
(GC::AllocBytes + 1023) >> 10,
|
||||||
(GC::Threshold + 1023) >> 10,
|
|
||||||
(GC::Estimate + 1023) >> 10,
|
(GC::Estimate + 1023) >> 10,
|
||||||
GC::StepCount,
|
(GC::Threshold + 1023) >> 10);
|
||||||
(GC::MinStepSize + 1023) >> 10);
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FStepStats :: Reset
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FStepStats::Reset()
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < countof(Count); ++i)
|
||||||
|
{
|
||||||
|
Count[i] = 0;
|
||||||
|
BytesCovered[i] = 0;
|
||||||
|
Clock[i].Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FStepStats :: Format
|
||||||
|
//
|
||||||
|
// Appends its stats to the given FString.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FStepStats::Format(FString &out)
|
||||||
|
{
|
||||||
|
// Because everything in the default green is hard to distinguish,
|
||||||
|
// each stage has its own color.
|
||||||
|
for (int i = GC::GCS_Propagate; i < GC::GCS_Done; ++i)
|
||||||
|
{
|
||||||
|
int count = Count[i];
|
||||||
|
double time = Clock[i].TimeMS();
|
||||||
|
out.AppendFormat(TEXTCOLOR_ESCAPESTR "%c[%c%6zuK %4d*%.2fms]",
|
||||||
|
"-NKB"[i], /* Color codes */
|
||||||
|
"-PSD"[i], /* Stage prefixes: (P)ropagate, (S)weep, (D)estroy */
|
||||||
|
(BytesCovered[i] + 1023) >> 10, count, count != 0 ? time / count : time);
|
||||||
|
}
|
||||||
|
out << TEXTCOLOR_GREEN;
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// CCMD gc
|
// CCMD gc
|
||||||
|
|
|
@ -37,12 +37,21 @@ namespace GC
|
||||||
GCS_Pause,
|
GCS_Pause,
|
||||||
GCS_Propagate,
|
GCS_Propagate,
|
||||||
GCS_Sweep,
|
GCS_Sweep,
|
||||||
GCS_Finalize
|
GCS_Destroy,
|
||||||
|
GCS_Done,
|
||||||
|
|
||||||
|
GCS_COUNT
|
||||||
};
|
};
|
||||||
|
|
||||||
// Number of bytes currently allocated through M_Malloc/M_Realloc.
|
// Number of bytes currently allocated through M_Malloc/M_Realloc.
|
||||||
extern size_t AllocBytes;
|
extern size_t AllocBytes;
|
||||||
|
|
||||||
|
// Number of bytes allocated since last collection step.
|
||||||
|
extern size_t RunningAllocBytes;
|
||||||
|
|
||||||
|
// Number of bytes freed since last collection step.
|
||||||
|
extern size_t RunningDeallocBytes;
|
||||||
|
|
||||||
// Amount of memory to allocate before triggering a collection.
|
// Amount of memory to allocate before triggering a collection.
|
||||||
extern size_t Threshold;
|
extern size_t Threshold;
|
||||||
|
|
||||||
|
@ -70,18 +79,12 @@ namespace GC
|
||||||
// Is this the final collection just before exit?
|
// Is this the final collection just before exit?
|
||||||
extern bool FinalGC;
|
extern bool FinalGC;
|
||||||
|
|
||||||
// Counts the number of times CheckGC has been called.
|
|
||||||
extern uint64_t CheckTime;
|
|
||||||
|
|
||||||
// Current white value for known-dead objects.
|
// Current white value for known-dead objects.
|
||||||
static inline uint32_t OtherWhite()
|
static inline uint32_t OtherWhite()
|
||||||
{
|
{
|
||||||
return CurrentWhite ^ OF_WhiteBits;
|
return CurrentWhite ^ OF_WhiteBits;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frees all objects, whether they're dead or not.
|
|
||||||
void FreeAll();
|
|
||||||
|
|
||||||
// Does one collection step.
|
// Does one collection step.
|
||||||
void Step();
|
void Step();
|
||||||
|
|
||||||
|
@ -118,12 +121,7 @@ namespace GC
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if it's time to collect, and do a collection step if it is.
|
// Check if it's time to collect, and do a collection step if it is.
|
||||||
static inline void CheckGC()
|
void CheckGC();
|
||||||
{
|
|
||||||
CheckTime++;
|
|
||||||
if (AllocBytes >= Threshold)
|
|
||||||
Step();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Forces a collection to start now.
|
// Forces a collection to start now.
|
||||||
static inline void StartCollection()
|
static inline void StartCollection()
|
||||||
|
@ -176,6 +174,32 @@ namespace GC
|
||||||
using GCMarkerFunc = void(*)();
|
using GCMarkerFunc = void(*)();
|
||||||
void AddMarkerFunc(GCMarkerFunc func);
|
void AddMarkerFunc(GCMarkerFunc func);
|
||||||
|
|
||||||
|
// Report an allocation to the GC
|
||||||
|
static inline void ReportAlloc(size_t alloc)
|
||||||
|
{
|
||||||
|
AllocBytes += alloc;
|
||||||
|
RunningAllocBytes += alloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report a deallocation to the GC
|
||||||
|
static inline void ReportDealloc(size_t dealloc)
|
||||||
|
{
|
||||||
|
AllocBytes -= dealloc;
|
||||||
|
RunningDeallocBytes += dealloc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report a reallocation to the GC
|
||||||
|
static inline void ReportRealloc(size_t oldsize, size_t newsize)
|
||||||
|
{
|
||||||
|
if (oldsize < newsize)
|
||||||
|
{
|
||||||
|
ReportAlloc(newsize - oldsize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ReportDealloc(oldsize - newsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A template class to help with handling read barriers. It does not
|
// A template class to help with handling read barriers. It does not
|
||||||
|
|
|
@ -22,6 +22,7 @@ public:
|
||||||
|
|
||||||
OpenGLFrameBuffer(void *hMonitor, bool fullscreen) ;
|
OpenGLFrameBuffer(void *hMonitor, bool fullscreen) ;
|
||||||
~OpenGLFrameBuffer();
|
~OpenGLFrameBuffer();
|
||||||
|
int Backend() override { return 2; }
|
||||||
bool CompileNextShader() override;
|
bool CompileNextShader() override;
|
||||||
void InitializeState() override;
|
void InitializeState() override;
|
||||||
void Update() override;
|
void Update() override;
|
||||||
|
|
|
@ -484,12 +484,15 @@ int EncodeRegType(ExpEmit reg)
|
||||||
else if (reg.RegCount == 2)
|
else if (reg.RegCount == 2)
|
||||||
{
|
{
|
||||||
regtype |= REGT_MULTIREG2;
|
regtype |= REGT_MULTIREG2;
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (reg.RegCount == 3)
|
else if (reg.RegCount == 3)
|
||||||
{
|
{
|
||||||
regtype |= REGT_MULTIREG3;
|
regtype |= REGT_MULTIREG3;
|
||||||
}
|
}
|
||||||
|
else if (reg.RegCount == 4)
|
||||||
|
{
|
||||||
|
regtype |= REGT_MULTIREG4;
|
||||||
|
}
|
||||||
return regtype;
|
return regtype;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -573,19 +576,20 @@ ExpEmit FxConstant::Emit(VMFunctionBuilder *build)
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
FxVectorValue::FxVectorValue(FxExpression *x, FxExpression *y, FxExpression *z, const FScriptPosition &sc)
|
FxVectorValue::FxVectorValue(FxExpression *x, FxExpression *y, FxExpression *z, FxExpression* w, const FScriptPosition &sc)
|
||||||
:FxExpression(EFX_VectorValue, sc)
|
:FxExpression(EFX_VectorValue, sc)
|
||||||
{
|
{
|
||||||
xyz[0] = x;
|
xyzw[0] = x;
|
||||||
xyz[1] = y;
|
xyzw[1] = y;
|
||||||
xyz[2] = z;
|
xyzw[2] = z;
|
||||||
|
xyzw[3] = w;
|
||||||
isConst = false;
|
isConst = false;
|
||||||
ValueType = TypeVoid; // we do not know yet
|
ValueType = TypeVoid; // we do not know yet
|
||||||
}
|
}
|
||||||
|
|
||||||
FxVectorValue::~FxVectorValue()
|
FxVectorValue::~FxVectorValue()
|
||||||
{
|
{
|
||||||
for (auto &a : xyz)
|
for (auto &a : xyzw)
|
||||||
{
|
{
|
||||||
SAFE_DELETE(a);
|
SAFE_DELETE(a);
|
||||||
}
|
}
|
||||||
|
@ -595,7 +599,8 @@ FxExpression *FxVectorValue::Resolve(FCompileContext&ctx)
|
||||||
{
|
{
|
||||||
bool fails = false;
|
bool fails = false;
|
||||||
|
|
||||||
for (auto &a : xyz)
|
// Cast every scalar to float64
|
||||||
|
for (auto &a : xyzw)
|
||||||
{
|
{
|
||||||
if (a != nullptr)
|
if (a != nullptr)
|
||||||
{
|
{
|
||||||
|
@ -603,7 +608,7 @@ FxExpression *FxVectorValue::Resolve(FCompileContext&ctx)
|
||||||
if (a == nullptr) fails = true;
|
if (a == nullptr) fails = true;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (a->ValueType != TypeVector2) // a vec3 may be initialized with (vec2, z)
|
if (a->ValueType != TypeVector2 && a->ValueType != TypeVector3) // smaller vector can be used to initialize another vector
|
||||||
{
|
{
|
||||||
a = new FxFloatCast(a);
|
a = new FxFloatCast(a);
|
||||||
a = a->Resolve(ctx);
|
a = a->Resolve(ctx);
|
||||||
|
@ -612,51 +617,89 @@ FxExpression *FxVectorValue::Resolve(FCompileContext&ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fails)
|
if (fails)
|
||||||
{
|
{
|
||||||
delete this;
|
delete this;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
// at this point there are three legal cases:
|
|
||||||
// * two floats = vector2
|
// The actual dimension of the Vector does not correspond to the amount of non-null elements in xyzw
|
||||||
// * three floats = vector3
|
// For example: '(asdf.xy, 1)' would be Vector3 where xyzw[0]->ValueType == TypeVector2 and xyzw[1]->ValueType == TypeFloat64
|
||||||
// * vector2 + float = vector3
|
|
||||||
if (xyz[0]->ValueType == TypeVector2)
|
// Handle nesting and figure out the dimension of the vector
|
||||||
|
int vectorDimensions = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < maxVectorDimensions && xyzw[i]; ++i)
|
||||||
{
|
{
|
||||||
if (xyz[1]->ValueType != TypeFloat64 || xyz[2] != nullptr)
|
assert(dynamic_cast<FxExpression*>(xyzw[i]));
|
||||||
|
|
||||||
|
if (xyzw[i]->ValueType == TypeFloat64)
|
||||||
|
{
|
||||||
|
vectorDimensions++;
|
||||||
|
}
|
||||||
|
else if (xyzw[i]->ValueType == TypeVector2 || xyzw[i]->ValueType == TypeVector3 || xyzw[i]->ValueType == TypeVector4)
|
||||||
|
{
|
||||||
|
// Solve nested vector
|
||||||
|
int regCount = xyzw[i]->ValueType->RegCount;
|
||||||
|
|
||||||
|
if (regCount + vectorDimensions > maxVectorDimensions)
|
||||||
|
{
|
||||||
|
vectorDimensions += regCount; // Show proper number
|
||||||
|
goto too_big;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nested initializer gets simplified
|
||||||
|
if (xyzw[i]->ExprType == EFX_VectorValue)
|
||||||
|
{
|
||||||
|
// Shifts current elements to leave space for unwrapping nested initialization
|
||||||
|
for (int l = maxVectorDimensions - 1; l > i; --l)
|
||||||
|
{
|
||||||
|
xyzw[l] = xyzw[l - regCount + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
auto vi = static_cast<FxVectorValue*>(xyzw[i]);
|
||||||
|
for (int j = 0; j < regCount; ++j)
|
||||||
|
{
|
||||||
|
xyzw[i + j] = vi->xyzw[j];
|
||||||
|
vi->xyzw[j] = nullptr; // Preserve object after 'delete vi;'
|
||||||
|
}
|
||||||
|
delete vi;
|
||||||
|
|
||||||
|
// We extracted something, let's iterate on that again:
|
||||||
|
--i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vectorDimensions += regCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
ScriptPosition.Message(MSG_ERROR, "Not a valid vector");
|
ScriptPosition.Message(MSG_ERROR, "Not a valid vector");
|
||||||
delete this;
|
delete this;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
ValueType = TypeVector3;
|
|
||||||
if (xyz[0]->ExprType == EFX_VectorValue)
|
|
||||||
{
|
|
||||||
// If two vector initializers are nested, unnest them now.
|
|
||||||
auto vi = static_cast<FxVectorValue*>(xyz[0]);
|
|
||||||
xyz[2] = xyz[1];
|
|
||||||
xyz[1] = vi->xyz[1];
|
|
||||||
xyz[0] = vi->xyz[0];
|
|
||||||
vi->xyz[0] = vi->xyz[1] = nullptr; // Don't delete our own expressions.
|
|
||||||
delete vi;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (xyz[0]->ValueType == TypeFloat64 && xyz[1]->ValueType == TypeFloat64)
|
|
||||||
|
switch (vectorDimensions)
|
||||||
{
|
{
|
||||||
ValueType = xyz[2] == nullptr ? TypeVector2 : TypeVector3;
|
case 2: ValueType = TypeVector2; break;
|
||||||
}
|
case 3: ValueType = TypeVector3; break;
|
||||||
else
|
case 4: ValueType = TypeVector4; break;
|
||||||
{
|
default:
|
||||||
ScriptPosition.Message(MSG_ERROR, "Not a valid vector");
|
too_big:;
|
||||||
|
ScriptPosition.Message(MSG_ERROR, "Vector of %d dimensions is not supported", vectorDimensions);
|
||||||
delete this;
|
delete this;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if all elements are constant. If so this can be emitted as a constant vector.
|
// check if all elements are constant. If so this can be emitted as a constant vector.
|
||||||
isConst = true;
|
isConst = true;
|
||||||
for (auto &a : xyz)
|
for (auto &a : xyzw)
|
||||||
{
|
{
|
||||||
if (a != nullptr && !a->isConstant()) isConst = false;
|
if (a && !a->isConstant()) isConst = false;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -674,100 +717,96 @@ static ExpEmit EmitKonst(VMFunctionBuilder *build, ExpEmit &emit)
|
||||||
|
|
||||||
ExpEmit FxVectorValue::Emit(VMFunctionBuilder *build)
|
ExpEmit FxVectorValue::Emit(VMFunctionBuilder *build)
|
||||||
{
|
{
|
||||||
// no const handling here. Ultimately it's too rarely used (i.e. the only fully constant vector ever allocated in ZDoom is the 0-vector in a very few places)
|
int vectorDimensions = ValueType->RegCount;
|
||||||
// and the negatives (excessive allocation of float constants) outweigh the positives (saved a few instructions)
|
int vectorElements = 0;
|
||||||
assert(xyz[0] != nullptr);
|
for (auto& e : xyzw)
|
||||||
assert(xyz[1] != nullptr);
|
|
||||||
if (ValueType == TypeVector2)
|
|
||||||
{
|
{
|
||||||
ExpEmit tempxval = xyz[0]->Emit(build);
|
if (e) vectorElements++;
|
||||||
ExpEmit tempyval = xyz[1]->Emit(build);
|
|
||||||
ExpEmit xval = EmitKonst(build, tempxval);
|
|
||||||
ExpEmit yval = EmitKonst(build, tempyval);
|
|
||||||
assert(xval.RegType == REGT_FLOAT && yval.RegType == REGT_FLOAT);
|
|
||||||
if (yval.RegNum == xval.RegNum + 1)
|
|
||||||
{
|
|
||||||
// The results are already in two continuous registers so just return them as-is.
|
|
||||||
xval.RegCount++;
|
|
||||||
return xval;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// The values are not in continuous registers so they need to be copied together now.
|
|
||||||
ExpEmit out(build, REGT_FLOAT, 2);
|
|
||||||
build->Emit(OP_MOVEF, out.RegNum, xval.RegNum);
|
|
||||||
build->Emit(OP_MOVEF, out.RegNum + 1, yval.RegNum);
|
|
||||||
xval.Free(build);
|
|
||||||
yval.Free(build);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (xyz[0]->ValueType == TypeVector2) // vec2+float
|
assert(vectorElements > 0);
|
||||||
|
|
||||||
|
ExpEmit* tempVal = (ExpEmit*)calloc(vectorElements, sizeof(ExpEmit));
|
||||||
|
ExpEmit* val = (ExpEmit*)calloc(vectorElements, sizeof(ExpEmit));
|
||||||
|
|
||||||
|
// Init ExpEmit
|
||||||
|
for (int i = 0; i < vectorElements; ++i)
|
||||||
{
|
{
|
||||||
ExpEmit xyval = xyz[0]->Emit(build);
|
new(tempVal + i) ExpEmit(xyzw[i]->Emit(build));
|
||||||
ExpEmit tempzval = xyz[1]->Emit(build);
|
new(val + i) ExpEmit(EmitKonst(build, tempVal[i]));
|
||||||
ExpEmit zval = EmitKonst(build, tempzval);
|
|
||||||
assert(xyval.RegType == REGT_FLOAT && xyval.RegCount == 2 && zval.RegType == REGT_FLOAT);
|
|
||||||
if (zval.RegNum == xyval.RegNum + 2)
|
|
||||||
{
|
|
||||||
// The results are already in three continuous registers so just return them as-is.
|
|
||||||
xyval.RegCount++;
|
|
||||||
return xyval;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// The values are not in continuous registers so they need to be copied together now.
|
|
||||||
ExpEmit out(build, REGT_FLOAT, 3);
|
|
||||||
build->Emit(OP_MOVEV2, out.RegNum, xyval.RegNum);
|
|
||||||
build->Emit(OP_MOVEF, out.RegNum + 2, zval.RegNum);
|
|
||||||
xyval.Free(build);
|
|
||||||
zval.Free(build);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else // 3*float
|
|
||||||
{
|
{
|
||||||
assert(xyz[2] != nullptr);
|
bool isContinuous = true;
|
||||||
ExpEmit tempxval = xyz[0]->Emit(build);
|
|
||||||
ExpEmit tempyval = xyz[1]->Emit(build);
|
for (int i = 1; i < vectorElements; ++i)
|
||||||
ExpEmit tempzval = xyz[2]->Emit(build);
|
|
||||||
ExpEmit xval = EmitKonst(build, tempxval);
|
|
||||||
ExpEmit yval = EmitKonst(build, tempyval);
|
|
||||||
ExpEmit zval = EmitKonst(build, tempzval);
|
|
||||||
assert(xval.RegType == REGT_FLOAT && yval.RegType == REGT_FLOAT && zval.RegType == REGT_FLOAT);
|
|
||||||
if (yval.RegNum == xval.RegNum + 1 && zval.RegNum == xval.RegNum + 2)
|
|
||||||
{
|
{
|
||||||
// The results are already in three continuous registers so just return them as-is.
|
if (val[i - 1].RegNum + val[i - 1].RegCount != val[i].RegNum)
|
||||||
xval.RegCount += 2;
|
|
||||||
return xval;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// The values are not in continuous registers so they need to be copied together now.
|
|
||||||
ExpEmit out(build, REGT_FLOAT, 3);
|
|
||||||
//Try to optimize a bit...
|
|
||||||
if (yval.RegNum == xval.RegNum + 1)
|
|
||||||
{
|
{
|
||||||
build->Emit(OP_MOVEV2, out.RegNum, xval.RegNum);
|
isContinuous = false;
|
||||||
build->Emit(OP_MOVEF, out.RegNum + 2, zval.RegNum);
|
break;
|
||||||
}
|
}
|
||||||
else if (zval.RegNum == yval.RegNum + 1)
|
}
|
||||||
|
|
||||||
|
// all values are in continuous registers:
|
||||||
|
if (isContinuous)
|
||||||
|
{
|
||||||
|
val[0].RegCount = vectorDimensions;
|
||||||
|
return val[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpEmit out(build, REGT_FLOAT, vectorDimensions);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto emitRegMove = [&](int regsToMove, int dstRegIndex, int srcRegIndex) {
|
||||||
|
assert(dstRegIndex < vectorDimensions);
|
||||||
|
assert(srcRegIndex < vectorDimensions);
|
||||||
|
assert(regsToMove > 0 && regsToMove <= 4);
|
||||||
|
build->Emit(regsToMove == 1 ? OP_MOVEF : OP_MOVEV2 + regsToMove - 2, out.RegNum + dstRegIndex, val[srcRegIndex].RegNum);
|
||||||
|
static_assert(OP_MOVEV2 + 1 == OP_MOVEV3);
|
||||||
|
static_assert(OP_MOVEV3 + 1 == OP_MOVEV4);
|
||||||
|
};
|
||||||
|
|
||||||
|
int regsToPush = 0;
|
||||||
|
int nextRegNum = val[0].RegNum;
|
||||||
|
int lastElementIndex = 0;
|
||||||
|
int reg = 0;
|
||||||
|
|
||||||
|
// Use larger MOVE OPs for any groups of registers that are continuous including those across individual xyzw[] elements
|
||||||
|
for (int elementIndex = 0; elementIndex < vectorElements; ++elementIndex)
|
||||||
|
{
|
||||||
|
int regCount = xyzw[elementIndex]->ValueType->RegCount;
|
||||||
|
|
||||||
|
if (nextRegNum != val[elementIndex].RegNum)
|
||||||
{
|
{
|
||||||
build->Emit(OP_MOVEF, out.RegNum, xval.RegNum);
|
emitRegMove(regsToPush, reg, lastElementIndex);
|
||||||
build->Emit(OP_MOVEV2, out.RegNum+1, yval.RegNum);
|
|
||||||
|
reg += regsToPush;
|
||||||
|
regsToPush = regCount;
|
||||||
|
nextRegNum = val[elementIndex].RegNum + val[elementIndex].RegCount;
|
||||||
|
lastElementIndex = elementIndex;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
build->Emit(OP_MOVEF, out.RegNum, xval.RegNum);
|
regsToPush += regCount;
|
||||||
build->Emit(OP_MOVEF, out.RegNum + 1, yval.RegNum);
|
nextRegNum = val[elementIndex].RegNum + val[elementIndex].RegCount;
|
||||||
build->Emit(OP_MOVEF, out.RegNum + 2, zval.RegNum);
|
|
||||||
}
|
}
|
||||||
xval.Free(build);
|
}
|
||||||
yval.Free(build);
|
|
||||||
zval.Free(build);
|
// Emit move instructions on the last register
|
||||||
return out;
|
if (regsToPush > 0)
|
||||||
|
{
|
||||||
|
emitRegMove(regsToPush, reg, lastElementIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < vectorElements; ++i)
|
||||||
|
{
|
||||||
|
val[i].Free(build);
|
||||||
|
val[i].~ExpEmit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -1688,7 +1727,7 @@ FxExpression *FxTypeCast::Resolve(FCompileContext &ctx)
|
||||||
delete this;
|
delete this;
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
else if ((basex->IsVector2() && IsVector2()) || (basex->IsVector3() && IsVector3()))
|
else if ((basex->IsVector2() && IsVector2()) || (basex->IsVector3() && IsVector3()) || (basex->IsVector4() && IsVector4()))
|
||||||
{
|
{
|
||||||
auto x = basex;
|
auto x = basex;
|
||||||
basex = nullptr;
|
basex = nullptr;
|
||||||
|
@ -1887,6 +1926,10 @@ ExpEmit FxMinusSign::Emit(VMFunctionBuilder *build)
|
||||||
build->Emit(OP_NEGV3, to.RegNum, from.RegNum);
|
build->Emit(OP_NEGV3, to.RegNum, from.RegNum);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
build->Emit(OP_NEGV4, to.RegNum, from.RegNum);
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return to;
|
return to;
|
||||||
|
@ -2799,7 +2842,7 @@ FxExpression *FxAddSub::Resolve(FCompileContext& ctx)
|
||||||
else if (left->IsVector() && right->IsVector())
|
else if (left->IsVector() && right->IsVector())
|
||||||
{
|
{
|
||||||
// a vector2 can be added to or subtracted from a vector 3 but it needs to be the right operand.
|
// a vector2 can be added to or subtracted from a vector 3 but it needs to be the right operand.
|
||||||
if (((left->IsVector3() || left->IsVector2()) && right->IsVector2()) || (left->IsVector3() && right->IsVector3()))
|
if (((left->IsVector3() || left->IsVector2()) && right->IsVector2()) || (left->IsVector3() && right->IsVector3()) || (left->IsVector4() && right->IsVector4()))
|
||||||
{
|
{
|
||||||
ValueType = left->ValueType;
|
ValueType = left->ValueType;
|
||||||
}
|
}
|
||||||
|
@ -2893,7 +2936,7 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build)
|
||||||
{
|
{
|
||||||
assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT);
|
assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT);
|
||||||
|
|
||||||
build->Emit(right->IsVector2() ? OP_ADDV2_RR : OP_ADDV3_RR, to.RegNum, op1.RegNum, op2.RegNum);
|
build->Emit(right->IsVector4() ? OP_ADDV4_RR : right->IsVector3() ? OP_ADDV3_RR : OP_ADDV2_RR, to.RegNum, op1.RegNum, op2.RegNum);
|
||||||
if (left->IsVector3() && right->IsVector2() && to.RegNum != op1.RegNum)
|
if (left->IsVector3() && right->IsVector2() && to.RegNum != op1.RegNum)
|
||||||
{
|
{
|
||||||
// must move the z-coordinate
|
// must move the z-coordinate
|
||||||
|
@ -2926,7 +2969,7 @@ ExpEmit FxAddSub::Emit(VMFunctionBuilder *build)
|
||||||
if (IsVector())
|
if (IsVector())
|
||||||
{
|
{
|
||||||
assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT);
|
assert(op1.RegType == REGT_FLOAT && op2.RegType == REGT_FLOAT);
|
||||||
build->Emit(right->IsVector2() ? OP_SUBV2_RR : OP_SUBV3_RR, to.RegNum, op1.RegNum, op2.RegNum);
|
build->Emit(right->IsVector4() ? OP_SUBV4_RR : right->IsVector3() ? OP_SUBV3_RR : OP_SUBV2_RR, to.RegNum, op1.RegNum, op2.RegNum);
|
||||||
return to;
|
return to;
|
||||||
}
|
}
|
||||||
else if (ValueType->GetRegType() == REGT_FLOAT)
|
else if (ValueType->GetRegType() == REGT_FLOAT)
|
||||||
|
@ -3129,11 +3172,11 @@ ExpEmit FxMulDiv::Emit(VMFunctionBuilder *build)
|
||||||
int op;
|
int op;
|
||||||
if (op2.Konst)
|
if (op2.Konst)
|
||||||
{
|
{
|
||||||
op = Operator == '*' ? (IsVector2() ? OP_MULVF2_RK : OP_MULVF3_RK) : (IsVector2() ? OP_DIVVF2_RK : OP_DIVVF3_RK);
|
op = Operator == '*' ? (IsVector2() ? OP_MULVF2_RK : IsVector3() ? OP_MULVF3_RK : OP_MULVF4_RK) : (IsVector2() ? OP_DIVVF2_RK : IsVector3() ? OP_DIVVF3_RK : OP_DIVVF4_RK);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
op = Operator == '*' ? (IsVector2() ? OP_MULVF2_RR : OP_MULVF3_RR) : (IsVector2() ? OP_DIVVF2_RR : OP_DIVVF3_RR);
|
op = Operator == '*' ? (IsVector2() ? OP_MULVF2_RR : IsVector3() ? OP_MULVF3_RR : OP_MULVF4_RR) : (IsVector2() ? OP_DIVVF2_RR : IsVector3() ? OP_DIVVF3_RR : OP_DIVVF4_RR);
|
||||||
}
|
}
|
||||||
op1.Free(build);
|
op1.Free(build);
|
||||||
op2.Free(build);
|
op2.Free(build);
|
||||||
|
@ -3598,7 +3641,7 @@ FxExpression *FxCompareEq::Resolve(FCompileContext& ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// identical types are always comparable, if they can be placed in a register, so we can save most checks if this is the case.
|
// identical types are always comparable, if they can be placed in a register, so we can save most checks if this is the case.
|
||||||
if (left->ValueType != right->ValueType && !(left->IsVector2() && right->IsVector2()) && !(left->IsVector3() && right->IsVector3()))
|
if (left->ValueType != right->ValueType && !(left->IsVector2() && right->IsVector2()) && !(left->IsVector3() && right->IsVector3()) && !(left->IsVector4() && right->IsVector4()))
|
||||||
{
|
{
|
||||||
FxExpression *x;
|
FxExpression *x;
|
||||||
if (left->IsNumeric() && right->ValueType == TypeString && (x = StringConstToChar(right)))
|
if (left->IsNumeric() && right->ValueType == TypeString && (x = StringConstToChar(right)))
|
||||||
|
@ -3836,11 +3879,11 @@ ExpEmit FxCompareEq::EmitCommon(VMFunctionBuilder *build, bool forcompare, bool
|
||||||
std::swap(op1, op2);
|
std::swap(op1, op2);
|
||||||
}
|
}
|
||||||
assert(!op1.Konst);
|
assert(!op1.Konst);
|
||||||
assert(op1.RegCount >= 1 && op1.RegCount <= 3);
|
assert(op1.RegCount >= 1 && op1.RegCount <= 4);
|
||||||
|
|
||||||
ExpEmit to(build, REGT_INT);
|
ExpEmit to(build, REGT_INT);
|
||||||
|
|
||||||
static int flops[] = { OP_EQF_R, OP_EQV2_R, OP_EQV3_R };
|
static int flops[] = { OP_EQF_R, OP_EQV2_R, OP_EQV3_R, OP_EQV4_R };
|
||||||
instr = op1.RegType == REGT_INT ? OP_EQ_R :
|
instr = op1.RegType == REGT_INT ? OP_EQ_R :
|
||||||
op1.RegType == REGT_FLOAT ? flops[op1.RegCount - 1] :
|
op1.RegType == REGT_FLOAT ? flops[op1.RegCount - 1] :
|
||||||
OP_EQA_R;
|
OP_EQA_R;
|
||||||
|
@ -4256,7 +4299,7 @@ ExpEmit FxConcat::Emit(VMFunctionBuilder *build)
|
||||||
build->Emit(op1.RegType == REGT_INT ? OP_LK : op1.RegType == REGT_FLOAT ? OP_LKF : OP_LKP, nonconst.RegNum, op1.RegNum);
|
build->Emit(op1.RegType == REGT_INT ? OP_LK : op1.RegType == REGT_FLOAT ? OP_LKF : OP_LKP, nonconst.RegNum, op1.RegNum);
|
||||||
op1 = nonconst;
|
op1 = nonconst;
|
||||||
}
|
}
|
||||||
if (op1.RegType == REGT_FLOAT) cast = op1.RegCount == 1 ? CAST_F2S : op1.RegCount == 2 ? CAST_V22S : CAST_V32S;
|
if (op1.RegType == REGT_FLOAT) cast = op1.RegCount == 1 ? CAST_F2S : op1.RegCount == 2 ? CAST_V22S : op1.RegCount == 3 ? CAST_V32S : CAST_V42S;
|
||||||
else if (left->ValueType == TypeUInt32) cast = CAST_U2S;
|
else if (left->ValueType == TypeUInt32) cast = CAST_U2S;
|
||||||
else if (left->ValueType == TypeName) cast = CAST_N2S;
|
else if (left->ValueType == TypeName) cast = CAST_N2S;
|
||||||
else if (left->ValueType == TypeSound) cast = CAST_So2S;
|
else if (left->ValueType == TypeSound) cast = CAST_So2S;
|
||||||
|
@ -4289,7 +4332,7 @@ ExpEmit FxConcat::Emit(VMFunctionBuilder *build)
|
||||||
build->Emit(op2.RegType == REGT_INT ? OP_LK : op2.RegType == REGT_FLOAT ? OP_LKF : OP_LKP, nonconst.RegNum, op2.RegNum);
|
build->Emit(op2.RegType == REGT_INT ? OP_LK : op2.RegType == REGT_FLOAT ? OP_LKF : OP_LKP, nonconst.RegNum, op2.RegNum);
|
||||||
op2 = nonconst;
|
op2 = nonconst;
|
||||||
}
|
}
|
||||||
if (op2.RegType == REGT_FLOAT) cast = op2.RegCount == 1 ? CAST_F2S : op2.RegCount == 2 ? CAST_V22S : CAST_V32S;
|
if (op1.RegType == REGT_FLOAT) cast = op1.RegCount == 1 ? CAST_F2S : op1.RegCount == 2 ? CAST_V22S : op1.RegCount == 3 ? CAST_V32S : CAST_V42S;
|
||||||
else if (right->ValueType == TypeUInt32) cast = CAST_U2S;
|
else if (right->ValueType == TypeUInt32) cast = CAST_U2S;
|
||||||
else if (right->ValueType == TypeName) cast = CAST_N2S;
|
else if (right->ValueType == TypeName) cast = CAST_N2S;
|
||||||
else if (right->ValueType == TypeSound) cast = CAST_So2S;
|
else if (right->ValueType == TypeSound) cast = CAST_So2S;
|
||||||
|
@ -4552,7 +4595,7 @@ ExpEmit FxDotCross::Emit(VMFunctionBuilder *build)
|
||||||
ExpEmit to(build, ValueType->GetRegType(), ValueType->GetRegCount());
|
ExpEmit to(build, ValueType->GetRegType(), ValueType->GetRegCount());
|
||||||
ExpEmit op1 = left->Emit(build);
|
ExpEmit op1 = left->Emit(build);
|
||||||
ExpEmit op2 = right->Emit(build);
|
ExpEmit op2 = right->Emit(build);
|
||||||
int op = Operator == TK_Cross ? OP_CROSSV_RR : left->ValueType == TypeVector3 ? OP_DOTV3_RR : OP_DOTV2_RR;
|
int op = Operator == TK_Cross ? OP_CROSSV_RR : left->ValueType == TypeVector4 ? OP_DOTV4_RR : left->ValueType == TypeVector3 ? OP_DOTV3_RR : OP_DOTV2_RR;
|
||||||
build->Emit(op, to.RegNum, op1.RegNum, op2.RegNum);
|
build->Emit(op, to.RegNum, op1.RegNum, op2.RegNum);
|
||||||
op1.Free(build);
|
op1.Free(build);
|
||||||
op2.Free(build);
|
op2.Free(build);
|
||||||
|
@ -8740,12 +8783,12 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Vectors need special treatment because they are not normal constants
|
// Vectors need special treatment because they are not normal constants
|
||||||
FxConstant *cs[3] = { nullptr };
|
FxConstant *cs[4] = { nullptr };
|
||||||
for (int l = 0; l < ntype->GetRegCount(); l++)
|
for (int l = 0; l < ntype->GetRegCount(); l++)
|
||||||
{
|
{
|
||||||
cs[l] = new FxConstant(TypeFloat64, defaults[l + i + k + skipdefs + implicit], ScriptPosition);
|
cs[l] = new FxConstant(TypeFloat64, defaults[l + i + k + skipdefs + implicit], ScriptPosition);
|
||||||
}
|
}
|
||||||
FxExpression *x = new FxVectorValue(cs[0], cs[1], cs[2], ScriptPosition);
|
FxExpression *x = new FxVectorValue(cs[0], cs[1], cs[2], cs[3], ScriptPosition);
|
||||||
ArgList.Insert(i + k, x);
|
ArgList.Insert(i + k, x);
|
||||||
skipdefs += ntype->GetRegCount() - 1;
|
skipdefs += ntype->GetRegCount() - 1;
|
||||||
}
|
}
|
||||||
|
@ -9155,15 +9198,19 @@ ExpEmit FxVectorBuiltin::Emit(VMFunctionBuilder *build)
|
||||||
{
|
{
|
||||||
ExpEmit to(build, ValueType->GetRegType(), ValueType->GetRegCount());
|
ExpEmit to(build, ValueType->GetRegType(), ValueType->GetRegCount());
|
||||||
ExpEmit op = Self->Emit(build);
|
ExpEmit op = Self->Emit(build);
|
||||||
|
|
||||||
|
const int vecSize = (Self->ValueType == TypeVector2 || Self->ValueType == TypeFVector2) ? 2
|
||||||
|
: (Self->ValueType == TypeVector3 || Self->ValueType == TypeFVector3) ? 3 : 4;
|
||||||
|
|
||||||
if (Function == NAME_Length)
|
if (Function == NAME_Length)
|
||||||
{
|
{
|
||||||
build->Emit(Self->ValueType == TypeVector2 || Self->ValueType == TypeFVector2 ? OP_LENV2 : OP_LENV3, to.RegNum, op.RegNum);
|
build->Emit(vecSize == 2 ? OP_LENV2 : vecSize == 3 ? OP_LENV3 : OP_LENV4, to.RegNum, op.RegNum);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ExpEmit len(build, REGT_FLOAT);
|
ExpEmit len(build, REGT_FLOAT);
|
||||||
build->Emit(Self->ValueType == TypeVector2 || Self->ValueType == TypeFVector2 ? OP_LENV2 : OP_LENV3, len.RegNum, op.RegNum);
|
build->Emit(vecSize == 2 ? OP_LENV2 : vecSize == 3 ? OP_LENV3 : OP_LENV4, len.RegNum, op.RegNum);
|
||||||
build->Emit(Self->ValueType == TypeVector2 || Self->ValueType == TypeFVector2 ? OP_DIVVF2_RR : OP_DIVVF3_RR, to.RegNum, op.RegNum, len.RegNum);
|
build->Emit(vecSize == 2 ? OP_DIVVF2_RR : vecSize == 3 ? OP_DIVVF3_RR : OP_DIVVF4_RR, to.RegNum, op.RegNum, len.RegNum);
|
||||||
len.Free(build);
|
len.Free(build);
|
||||||
}
|
}
|
||||||
op.Free(build);
|
op.Free(build);
|
||||||
|
@ -10827,11 +10874,12 @@ FxLocalVariableDeclaration::FxLocalVariableDeclaration(PType *type, FName name,
|
||||||
// Local FVector isn't different from Vector
|
// Local FVector isn't different from Vector
|
||||||
if (type == TypeFVector2) type = TypeVector2;
|
if (type == TypeFVector2) type = TypeVector2;
|
||||||
else if (type == TypeFVector3) type = TypeVector3;
|
else if (type == TypeFVector3) type = TypeVector3;
|
||||||
|
else if (type == TypeFVector4) type = TypeVector4;
|
||||||
|
|
||||||
ValueType = type;
|
ValueType = type;
|
||||||
VarFlags = varflags;
|
VarFlags = varflags;
|
||||||
Name = name;
|
Name = name;
|
||||||
RegCount = type == TypeVector2 ? 2 : type == TypeVector3 ? 3 : 1;
|
RegCount = type->RegCount;
|
||||||
Init = initval;
|
Init = initval;
|
||||||
clearExpr = nullptr;
|
clearExpr = nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -336,9 +336,10 @@ public:
|
||||||
bool IsFloat() const { return ValueType->isFloat(); }
|
bool IsFloat() const { return ValueType->isFloat(); }
|
||||||
bool IsInteger() const { return ValueType->isNumeric() && ValueType->isIntCompatible(); }
|
bool IsInteger() const { return ValueType->isNumeric() && ValueType->isIntCompatible(); }
|
||||||
bool IsPointer() const { return ValueType->isPointer(); }
|
bool IsPointer() const { return ValueType->isPointer(); }
|
||||||
bool IsVector() const { return ValueType == TypeVector2 || ValueType == TypeVector3 || ValueType == TypeFVector2 || ValueType == TypeFVector3; };
|
bool IsVector() const { return IsVector2() || IsVector3() || IsVector4(); };
|
||||||
bool IsVector2() const { return ValueType == TypeVector2 || ValueType == TypeFVector2; };
|
bool IsVector2() const { return ValueType == TypeVector2 || ValueType == TypeFVector2; };
|
||||||
bool IsVector3() const { return ValueType == TypeVector3 || ValueType == TypeFVector3; };
|
bool IsVector3() const { return ValueType == TypeVector3 || ValueType == TypeFVector3; };
|
||||||
|
bool IsVector4() const { return ValueType == TypeVector4 || ValueType == TypeFVector4; };
|
||||||
bool IsBoolCompat() const { return ValueType->isScalar(); }
|
bool IsBoolCompat() const { return ValueType->isScalar(); }
|
||||||
bool IsObject() const { return ValueType->isObjectPointer(); }
|
bool IsObject() const { return ValueType->isObjectPointer(); }
|
||||||
bool IsArray() const { return ValueType->isArray() || (ValueType->isPointer() && ValueType->toPointer()->PointedType->isArray()); }
|
bool IsArray() const { return ValueType->isArray() || (ValueType->isPointer() && ValueType->toPointer()->PointedType->isArray()); }
|
||||||
|
@ -550,20 +551,23 @@ public:
|
||||||
|
|
||||||
class FxVectorValue : public FxExpression
|
class FxVectorValue : public FxExpression
|
||||||
{
|
{
|
||||||
FxExpression *xyz[3];
|
constexpr static int maxVectorDimensions = 4;
|
||||||
|
|
||||||
|
FxExpression *xyzw[maxVectorDimensions];
|
||||||
bool isConst; // gets set to true if all element are const (used by function defaults parser)
|
bool isConst; // gets set to true if all element are const (used by function defaults parser)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
friend class ZCCCompiler;
|
friend class ZCCCompiler;
|
||||||
|
|
||||||
FxVectorValue(FxExpression *x, FxExpression *y, FxExpression *z, const FScriptPosition &sc);
|
FxVectorValue(FxExpression *x, FxExpression *y, FxExpression *z, FxExpression* w, const FScriptPosition &sc);
|
||||||
~FxVectorValue();
|
~FxVectorValue();
|
||||||
FxExpression *Resolve(FCompileContext&);
|
FxExpression *Resolve(FCompileContext&);
|
||||||
bool isConstVector(int dim)
|
bool isConstVector(int dim)
|
||||||
{
|
{
|
||||||
if (!isConst) return false;
|
if (!isConst)
|
||||||
return dim == 2 ? xyz[2] == nullptr : xyz[2] != nullptr;
|
return false;
|
||||||
|
return dim >= 0 && dim <= maxVectorDimensions && xyzw[dim - 1] && (dim == maxVectorDimensions || !xyzw[dim]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExpEmit Emit(VMFunctionBuilder *build);
|
ExpEmit Emit(VMFunctionBuilder *build);
|
||||||
|
|
|
@ -637,6 +637,7 @@ size_t VMFunctionBuilder::Emit(int opcode, int opa, VM_SHALF opbc)
|
||||||
int chg;
|
int chg;
|
||||||
if (opa & REGT_MULTIREG2) chg = 2;
|
if (opa & REGT_MULTIREG2) chg = 2;
|
||||||
else if (opa & REGT_MULTIREG3) chg = 3;
|
else if (opa & REGT_MULTIREG3) chg = 3;
|
||||||
|
else if (opa & REGT_MULTIREG4) chg = 4;
|
||||||
else chg = 1;
|
else chg = 1;
|
||||||
ParamChange(chg);
|
ParamChange(chg);
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,8 +61,10 @@ PPointer *TypeFont;
|
||||||
PStateLabel *TypeStateLabel;
|
PStateLabel *TypeStateLabel;
|
||||||
PStruct *TypeVector2;
|
PStruct *TypeVector2;
|
||||||
PStruct *TypeVector3;
|
PStruct *TypeVector3;
|
||||||
|
PStruct* TypeVector4;
|
||||||
PStruct* TypeFVector2;
|
PStruct* TypeFVector2;
|
||||||
PStruct* TypeFVector3;
|
PStruct* TypeFVector3;
|
||||||
|
PStruct* TypeFVector4;
|
||||||
PStruct *TypeColorStruct;
|
PStruct *TypeColorStruct;
|
||||||
PStruct *TypeStringStruct;
|
PStruct *TypeStringStruct;
|
||||||
PPointer *TypeNullPtr;
|
PPointer *TypeNullPtr;
|
||||||
|
@ -350,6 +352,22 @@ void PType::StaticInit()
|
||||||
TypeVector3->RegCount = 3;
|
TypeVector3->RegCount = 3;
|
||||||
TypeVector3->isOrdered = true;
|
TypeVector3->isOrdered = true;
|
||||||
|
|
||||||
|
TypeVector4 = new PStruct(NAME_Vector4, nullptr);
|
||||||
|
TypeVector4->AddField(NAME_X, TypeFloat64);
|
||||||
|
TypeVector4->AddField(NAME_Y, TypeFloat64);
|
||||||
|
TypeVector4->AddField(NAME_Z, TypeFloat64);
|
||||||
|
TypeVector4->AddField(NAME_W, TypeFloat64);
|
||||||
|
// allow accessing xyz as a vector3. This is not supposed to be serialized so it's marked transient
|
||||||
|
TypeVector4->Symbols.AddSymbol(Create<PField>(NAME_XYZ, TypeVector3, VARF_Transient, 0));
|
||||||
|
TypeVector4->Symbols.AddSymbol(Create<PField>(NAME_XY, TypeVector2, VARF_Transient, 0));
|
||||||
|
TypeTable.AddType(TypeVector4, NAME_Struct);
|
||||||
|
TypeVector4->loadOp = OP_LV4;
|
||||||
|
TypeVector4->storeOp = OP_SV4;
|
||||||
|
TypeVector4->moveOp = OP_MOVEV4;
|
||||||
|
TypeVector4->RegType = REGT_FLOAT;
|
||||||
|
TypeVector4->RegCount = 4;
|
||||||
|
TypeVector4->isOrdered = true;
|
||||||
|
|
||||||
|
|
||||||
TypeFVector2 = new PStruct(NAME_FVector2, nullptr);
|
TypeFVector2 = new PStruct(NAME_FVector2, nullptr);
|
||||||
TypeFVector2->AddField(NAME_X, TypeFloat32);
|
TypeFVector2->AddField(NAME_X, TypeFloat32);
|
||||||
|
@ -376,6 +394,22 @@ void PType::StaticInit()
|
||||||
TypeFVector3->RegCount = 3;
|
TypeFVector3->RegCount = 3;
|
||||||
TypeFVector3->isOrdered = true;
|
TypeFVector3->isOrdered = true;
|
||||||
|
|
||||||
|
TypeFVector4 = new PStruct(NAME_FVector4, nullptr);
|
||||||
|
TypeFVector4->AddField(NAME_X, TypeFloat32);
|
||||||
|
TypeFVector4->AddField(NAME_Y, TypeFloat32);
|
||||||
|
TypeFVector4->AddField(NAME_Z, TypeFloat32);
|
||||||
|
TypeFVector4->AddField(NAME_W, TypeFloat32);
|
||||||
|
// allow accessing xyz as a vector3
|
||||||
|
TypeFVector4->Symbols.AddSymbol(Create<PField>(NAME_XYZ, TypeFVector3, VARF_Transient, 0));
|
||||||
|
TypeFVector4->Symbols.AddSymbol(Create<PField>(NAME_XY, TypeFVector2, VARF_Transient, 0));
|
||||||
|
TypeTable.AddType(TypeFVector4, NAME_Struct);
|
||||||
|
TypeFVector4->loadOp = OP_LFV4;
|
||||||
|
TypeFVector4->storeOp = OP_SFV4;
|
||||||
|
TypeFVector4->moveOp = OP_MOVEV4;
|
||||||
|
TypeFVector4->RegType = REGT_FLOAT;
|
||||||
|
TypeFVector4->RegCount = 4;
|
||||||
|
TypeFVector4->isOrdered = true;
|
||||||
|
|
||||||
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_sByte, TypeSInt8));
|
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_sByte, TypeSInt8));
|
||||||
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_Byte, TypeUInt8));
|
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_Byte, TypeUInt8));
|
||||||
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_Short, TypeSInt16));
|
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_Short, TypeSInt16));
|
||||||
|
@ -394,8 +428,10 @@ void PType::StaticInit()
|
||||||
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_State, TypeState));
|
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_State, TypeState));
|
||||||
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_Vector2, TypeVector2));
|
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_Vector2, TypeVector2));
|
||||||
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_Vector3, TypeVector3));
|
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_Vector3, TypeVector3));
|
||||||
|
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_Vector4, TypeVector4));
|
||||||
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_FVector2, TypeFVector2));
|
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_FVector2, TypeFVector2));
|
||||||
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_FVector3, TypeFVector3));
|
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_FVector3, TypeFVector3));
|
||||||
|
Namespaces.GlobalNamespace->Symbols.AddSymbol(Create<PSymbolType>(NAME_FVector4, TypeFVector4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -615,8 +615,10 @@ extern PTextureID *TypeTextureID;
|
||||||
extern PSpriteID *TypeSpriteID;
|
extern PSpriteID *TypeSpriteID;
|
||||||
extern PStruct* TypeVector2;
|
extern PStruct* TypeVector2;
|
||||||
extern PStruct* TypeVector3;
|
extern PStruct* TypeVector3;
|
||||||
|
extern PStruct* TypeVector4;
|
||||||
extern PStruct* TypeFVector2;
|
extern PStruct* TypeFVector2;
|
||||||
extern PStruct* TypeFVector3;
|
extern PStruct* TypeFVector3;
|
||||||
|
extern PStruct* TypeFVector4;
|
||||||
extern PStruct *TypeColorStruct;
|
extern PStruct *TypeColorStruct;
|
||||||
extern PStruct *TypeStringStruct;
|
extern PStruct *TypeStringStruct;
|
||||||
extern PStatePointer *TypeState;
|
extern PStatePointer *TypeState;
|
||||||
|
|
|
@ -639,6 +639,8 @@ static int print_reg(FILE *out, int col, int arg, int mode, int immshift, const
|
||||||
return col+printf_wrapper(out, "v%d.2", regnum);
|
return col+printf_wrapper(out, "v%d.2", regnum);
|
||||||
case REGT_FLOAT | REGT_MULTIREG3:
|
case REGT_FLOAT | REGT_MULTIREG3:
|
||||||
return col+printf_wrapper(out, "v%d.3", regnum);
|
return col+printf_wrapper(out, "v%d.3", regnum);
|
||||||
|
case REGT_FLOAT | REGT_MULTIREG4:
|
||||||
|
return col+printf_wrapper(out, "v%d.4", regnum);
|
||||||
case REGT_INT | REGT_KONST:
|
case REGT_INT | REGT_KONST:
|
||||||
return col+print_reg(out, 0, regnum, MODE_KI, 0, func);
|
return col+print_reg(out, 0, regnum, MODE_KI, 0, func);
|
||||||
case REGT_FLOAT | REGT_KONST:
|
case REGT_FLOAT | REGT_KONST:
|
||||||
|
|
|
@ -53,6 +53,7 @@ static const char *BuiltInTypeNames[] =
|
||||||
"string",
|
"string",
|
||||||
"vector2",
|
"vector2",
|
||||||
"vector3",
|
"vector3",
|
||||||
|
"vector4",
|
||||||
"name",
|
"name",
|
||||||
|
|
||||||
"color",
|
"color",
|
||||||
|
@ -684,6 +685,7 @@ static void PrintVectorInitializer(FLispString &out, ZCC_TreeNode *node)
|
||||||
PrintNodes(out, enode->X);
|
PrintNodes(out, enode->X);
|
||||||
PrintNodes(out, enode->Y);
|
PrintNodes(out, enode->Y);
|
||||||
PrintNodes(out, enode->Z);
|
PrintNodes(out, enode->Z);
|
||||||
|
PrintNodes(out, enode->W);
|
||||||
out.Close();
|
out.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -861,6 +861,7 @@ type_name1(X) ::= DOUBLE(T). { X.Int = ZCC_Float64; X.SourceLoc = T.SourceLoc
|
||||||
//type_name1(X) ::= STRING(T). { X.Int = ZCC_String; X.SourceLoc = T.SourceLoc; } // [ZZ] it's handled elsewhere. this particular line only causes troubles in the form of String.Format being invalid.
|
//type_name1(X) ::= STRING(T). { X.Int = ZCC_String; X.SourceLoc = T.SourceLoc; } // [ZZ] it's handled elsewhere. this particular line only causes troubles in the form of String.Format being invalid.
|
||||||
type_name1(X) ::= VECTOR2(T). { X.Int = ZCC_Vector2; X.SourceLoc = T.SourceLoc; }
|
type_name1(X) ::= VECTOR2(T). { X.Int = ZCC_Vector2; X.SourceLoc = T.SourceLoc; }
|
||||||
type_name1(X) ::= VECTOR3(T). { X.Int = ZCC_Vector3; X.SourceLoc = T.SourceLoc; }
|
type_name1(X) ::= VECTOR3(T). { X.Int = ZCC_Vector3; X.SourceLoc = T.SourceLoc; }
|
||||||
|
type_name1(X) ::= VECTOR4(T). { X.Int = ZCC_Vector4; X.SourceLoc = T.SourceLoc; }
|
||||||
type_name1(X) ::= NAME(T). { X.Int = ZCC_Name; X.SourceLoc = T.SourceLoc; }
|
type_name1(X) ::= NAME(T). { X.Int = ZCC_Name; X.SourceLoc = T.SourceLoc; }
|
||||||
type_name1(X) ::= SOUND(T). { X.Int = ZCC_Sound; X.SourceLoc = T.SourceLoc; }
|
type_name1(X) ::= SOUND(T). { X.Int = ZCC_Sound; X.SourceLoc = T.SourceLoc; }
|
||||||
type_name1(X) ::= STATE(T). { X.Int = ZCC_State; X.SourceLoc = T.SourceLoc; }
|
type_name1(X) ::= STATE(T). { X.Int = ZCC_State; X.SourceLoc = T.SourceLoc; }
|
||||||
|
@ -931,7 +932,7 @@ type_name(X) ::= DOT dottable_id(A).
|
||||||
/* Type names can also be used as identifiers in contexts where type names
|
/* Type names can also be used as identifiers in contexts where type names
|
||||||
* are not normally allowed. */
|
* are not normally allowed. */
|
||||||
%fallback IDENTIFIER
|
%fallback IDENTIFIER
|
||||||
SBYTE BYTE SHORT USHORT INT UINT BOOL FLOAT DOUBLE STRING VECTOR2 VECTOR3 NAME MAP ARRAY VOID STATE COLOR SOUND UINT8 INT8 UINT16 INT16 PROPERTY.
|
SBYTE BYTE SHORT USHORT INT UINT BOOL FLOAT DOUBLE STRING VECTOR2 VECTOR3 VECTOR4 NAME MAP ARRAY VOID STATE COLOR SOUND UINT8 INT8 UINT16 INT16 PROPERTY.
|
||||||
|
|
||||||
/* Aggregate types */
|
/* Aggregate types */
|
||||||
%type aggregate_type {ZCC_Type *}
|
%type aggregate_type {ZCC_Type *}
|
||||||
|
@ -1303,6 +1304,17 @@ primary(X) ::= SUPER(T).
|
||||||
X = expr;
|
X = expr;
|
||||||
}
|
}
|
||||||
primary(X) ::= constant(A). { X = A; /*X-overwrites-A*/ }
|
primary(X) ::= constant(A). { X = A; /*X-overwrites-A*/ }
|
||||||
|
primary(XX) ::= LPAREN expr(A) COMMA expr(B) COMMA expr(C) COMMA expr(D) RPAREN. [DOT]
|
||||||
|
{
|
||||||
|
NEW_AST_NODE(VectorValue, expr, A);
|
||||||
|
expr->Operation = PEX_Vector;
|
||||||
|
expr->Type = TypeVector4;
|
||||||
|
expr->X = A;
|
||||||
|
expr->Y = B;
|
||||||
|
expr->Z = C;
|
||||||
|
expr->W = D;
|
||||||
|
XX = expr;
|
||||||
|
}
|
||||||
primary(XX) ::= LPAREN expr(A) COMMA expr(B) COMMA expr(C) RPAREN. [DOT]
|
primary(XX) ::= LPAREN expr(A) COMMA expr(B) COMMA expr(C) RPAREN. [DOT]
|
||||||
{
|
{
|
||||||
NEW_AST_NODE(VectorValue, expr, A);
|
NEW_AST_NODE(VectorValue, expr, A);
|
||||||
|
@ -1311,6 +1323,7 @@ primary(XX) ::= LPAREN expr(A) COMMA expr(B) COMMA expr(C) RPAREN. [DOT]
|
||||||
expr->X = A;
|
expr->X = A;
|
||||||
expr->Y = B;
|
expr->Y = B;
|
||||||
expr->Z = C;
|
expr->Z = C;
|
||||||
|
expr->W = nullptr;
|
||||||
XX = expr;
|
XX = expr;
|
||||||
}
|
}
|
||||||
primary(XX) ::= LPAREN expr(A) COMMA expr(B) RPAREN. [DOT]
|
primary(XX) ::= LPAREN expr(A) COMMA expr(B) RPAREN. [DOT]
|
||||||
|
@ -1321,6 +1334,7 @@ primary(XX) ::= LPAREN expr(A) COMMA expr(B) RPAREN. [DOT]
|
||||||
expr->X = A;
|
expr->X = A;
|
||||||
expr->Y = B;
|
expr->Y = B;
|
||||||
expr->Z = nullptr;
|
expr->Z = nullptr;
|
||||||
|
expr->W = nullptr;
|
||||||
XX = expr;
|
XX = expr;
|
||||||
}
|
}
|
||||||
primary(X) ::= LPAREN expr(A) RPAREN.
|
primary(X) ::= LPAREN expr(A) RPAREN.
|
||||||
|
|
|
@ -1790,6 +1790,10 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
|
||||||
retval = TypeVector3;
|
retval = TypeVector3;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ZCC_Vector4:
|
||||||
|
retval = TypeVector4;
|
||||||
|
break;
|
||||||
|
|
||||||
case ZCC_State:
|
case ZCC_State:
|
||||||
retval = TypeState;
|
retval = TypeState;
|
||||||
break;
|
break;
|
||||||
|
@ -2150,7 +2154,7 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
auto type = DetermineType(c->Type(), f, f->Name, t, false, false);
|
auto type = DetermineType(c->Type(), f, f->Name, t, false, false);
|
||||||
if (type->isContainer() && type != TypeVector2 && type != TypeVector3 && type != TypeFVector2 && type != TypeFVector3)
|
if (type->isContainer() && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4)
|
||||||
{
|
{
|
||||||
// structs and classes only get passed by pointer.
|
// structs and classes only get passed by pointer.
|
||||||
type = NewPointer(type);
|
type = NewPointer(type);
|
||||||
|
@ -2168,6 +2172,10 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
||||||
{
|
{
|
||||||
type = TypeVector3;
|
type = TypeVector3;
|
||||||
}
|
}
|
||||||
|
else if (type == TypeFVector4)
|
||||||
|
{
|
||||||
|
type = TypeVector4;
|
||||||
|
}
|
||||||
// TBD: disallow certain types? For now, let everything pass that isn't an array.
|
// TBD: disallow certain types? For now, let everything pass that isn't an array.
|
||||||
rets.Push(type);
|
rets.Push(type);
|
||||||
t = static_cast<decltype(t)>(t->SiblingNext);
|
t = static_cast<decltype(t)>(t->SiblingNext);
|
||||||
|
@ -2340,12 +2348,12 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
int elementcount = 1;
|
int elementcount = 1;
|
||||||
TypedVMValue vmval[3]; // default is REGT_NIL which means 'no default value' here.
|
TypedVMValue vmval[4]; // default is REGT_NIL which means 'no default value' here.
|
||||||
if (p->Type != nullptr)
|
if (p->Type != nullptr)
|
||||||
{
|
{
|
||||||
auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false);
|
auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false);
|
||||||
int flags = 0;
|
int flags = 0;
|
||||||
if ((type->isStruct() && type != TypeVector2 && type != TypeVector3) || type->isDynArray())
|
if ((type->isStruct() && type != TypeVector2 && type != TypeVector3 && type != TypeVector4) || type->isDynArray())
|
||||||
{
|
{
|
||||||
// Structs are being passed by pointer, but unless marked 'out' that pointer must be readonly.
|
// Structs are being passed by pointer, but unless marked 'out' that pointer must be readonly.
|
||||||
type = NewPointer(type /*, !(p->Flags & ZCC_Out)*/);
|
type = NewPointer(type /*, !(p->Flags & ZCC_Out)*/);
|
||||||
|
@ -2362,8 +2370,12 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
||||||
{
|
{
|
||||||
elementcount = 3;
|
elementcount = 3;
|
||||||
}
|
}
|
||||||
|
else if (type == TypeVector4 || type == TypeFVector4)
|
||||||
|
{
|
||||||
|
elementcount = 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (type->GetRegType() == REGT_NIL && type != TypeVector2 && type != TypeVector3 && type != TypeFVector2 && type != TypeFVector3)
|
if (type->GetRegType() == REGT_NIL && type != TypeVector2 && type != TypeVector3 && type != TypeVector4 && type != TypeFVector2 && type != TypeFVector3 && type != TypeFVector4)
|
||||||
{
|
{
|
||||||
// If it's TypeError, then an error was already given
|
// If it's TypeError, then an error was already given
|
||||||
if (type != TypeError)
|
if (type != TypeError)
|
||||||
|
@ -2407,15 +2419,23 @@ void ZCCCompiler::CompileFunction(ZCC_StructWork *c, ZCC_FuncDeclarator *f, bool
|
||||||
if ((type == TypeVector2 || type == TypeFVector2) && x->ExprType == EFX_VectorValue && static_cast<FxVectorValue *>(x)->isConstVector(2))
|
if ((type == TypeVector2 || type == TypeFVector2) && x->ExprType == EFX_VectorValue && static_cast<FxVectorValue *>(x)->isConstVector(2))
|
||||||
{
|
{
|
||||||
auto vx = static_cast<FxVectorValue *>(x);
|
auto vx = static_cast<FxVectorValue *>(x);
|
||||||
vmval[0] = static_cast<FxConstant *>(vx->xyz[0])->GetValue().GetFloat();
|
vmval[0] = static_cast<FxConstant *>(vx->xyzw[0])->GetValue().GetFloat();
|
||||||
vmval[1] = static_cast<FxConstant *>(vx->xyz[1])->GetValue().GetFloat();
|
vmval[1] = static_cast<FxConstant *>(vx->xyzw[1])->GetValue().GetFloat();
|
||||||
}
|
}
|
||||||
else if ((type == TypeVector3 || type == TypeFVector3) && x->ExprType == EFX_VectorValue && static_cast<FxVectorValue *>(x)->isConstVector(3))
|
else if ((type == TypeVector3 || type == TypeFVector3) && x->ExprType == EFX_VectorValue && static_cast<FxVectorValue *>(x)->isConstVector(3))
|
||||||
{
|
{
|
||||||
auto vx = static_cast<FxVectorValue *>(x);
|
auto vx = static_cast<FxVectorValue *>(x);
|
||||||
vmval[0] = static_cast<FxConstant *>(vx->xyz[0])->GetValue().GetFloat();
|
vmval[0] = static_cast<FxConstant *>(vx->xyzw[0])->GetValue().GetFloat();
|
||||||
vmval[1] = static_cast<FxConstant *>(vx->xyz[1])->GetValue().GetFloat();
|
vmval[1] = static_cast<FxConstant *>(vx->xyzw[1])->GetValue().GetFloat();
|
||||||
vmval[2] = static_cast<FxConstant *>(vx->xyz[2])->GetValue().GetFloat();
|
vmval[2] = static_cast<FxConstant *>(vx->xyzw[2])->GetValue().GetFloat();
|
||||||
|
}
|
||||||
|
else if ((type == TypeVector4 || type == TypeFVector4) && x->ExprType == EFX_VectorValue && static_cast<FxVectorValue*>(x)->isConstVector(4))
|
||||||
|
{
|
||||||
|
auto vx = static_cast<FxVectorValue*>(x);
|
||||||
|
vmval[0] = static_cast<FxConstant*>(vx->xyzw[0])->GetValue().GetFloat();
|
||||||
|
vmval[1] = static_cast<FxConstant*>(vx->xyzw[1])->GetValue().GetFloat();
|
||||||
|
vmval[2] = static_cast<FxConstant*>(vx->xyzw[2])->GetValue().GetFloat();
|
||||||
|
vmval[3] = static_cast<FxConstant*>(vx->xyzw[3])->GetValue().GetFloat();
|
||||||
}
|
}
|
||||||
else if (!x->isConstant())
|
else if (!x->isConstant())
|
||||||
{
|
{
|
||||||
|
@ -3038,7 +3058,8 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast, bool substitute)
|
||||||
auto xx = ConvertNode(vecini->X);
|
auto xx = ConvertNode(vecini->X);
|
||||||
auto yy = ConvertNode(vecini->Y);
|
auto yy = ConvertNode(vecini->Y);
|
||||||
auto zz = ConvertNode(vecini->Z);
|
auto zz = ConvertNode(vecini->Z);
|
||||||
return new FxVectorValue(xx, yy, zz, *ast);
|
auto ww = ConvertNode(vecini->W);
|
||||||
|
return new FxVectorValue(xx, yy, zz, ww, *ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
case AST_LocalVarStmt:
|
case AST_LocalVarStmt:
|
||||||
|
|
|
@ -1297,7 +1297,8 @@ ZCC_TreeNode *TreeNodeDeepCopy_Internal(ZCC_AST *ast, ZCC_TreeNode *orig, bool c
|
||||||
// ZCC_VectorValue
|
// ZCC_VectorValue
|
||||||
copy->X = static_cast<ZCC_Expression *>(TreeNodeDeepCopy_Internal(ast, origCasted->X, true, copiedNodesList));
|
copy->X = static_cast<ZCC_Expression *>(TreeNodeDeepCopy_Internal(ast, origCasted->X, true, copiedNodesList));
|
||||||
copy->Y = static_cast<ZCC_Expression *>(TreeNodeDeepCopy_Internal(ast, origCasted->Y, true, copiedNodesList));
|
copy->Y = static_cast<ZCC_Expression *>(TreeNodeDeepCopy_Internal(ast, origCasted->Y, true, copiedNodesList));
|
||||||
copy->Z = static_cast<ZCC_Expression *>(TreeNodeDeepCopy_Internal(ast, origCasted->Z, true, copiedNodesList));
|
copy->Z = static_cast<ZCC_Expression*>(TreeNodeDeepCopy_Internal(ast, origCasted->Z, true, copiedNodesList));
|
||||||
|
copy->W = static_cast<ZCC_Expression*>(TreeNodeDeepCopy_Internal(ast, origCasted->W, true, copiedNodesList));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -158,6 +158,7 @@ enum EZCCBuiltinType
|
||||||
ZCC_String,
|
ZCC_String,
|
||||||
ZCC_Vector2,
|
ZCC_Vector2,
|
||||||
ZCC_Vector3,
|
ZCC_Vector3,
|
||||||
|
ZCC_Vector4,
|
||||||
ZCC_Name,
|
ZCC_Name,
|
||||||
|
|
||||||
ZCC_Color, // special types for ZDoom.
|
ZCC_Color, // special types for ZDoom.
|
||||||
|
@ -442,7 +443,7 @@ struct ZCC_ExprTrinary : ZCC_Expression
|
||||||
|
|
||||||
struct ZCC_VectorValue : ZCC_Expression
|
struct ZCC_VectorValue : ZCC_Expression
|
||||||
{
|
{
|
||||||
ZCC_Expression *X, *Y, *Z;
|
ZCC_Expression *X, *Y, *Z, *W;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ZCC_Statement : ZCC_TreeNode
|
struct ZCC_Statement : ZCC_TreeNode
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
extern PString *TypeString;
|
extern PString *TypeString;
|
||||||
extern PStruct *TypeVector2;
|
extern PStruct *TypeVector2;
|
||||||
extern PStruct *TypeVector3;
|
extern PStruct *TypeVector3;
|
||||||
|
extern PStruct* TypeVector4;
|
||||||
|
|
||||||
static void OutputJitLog(const asmjit::StringLogger &logger);
|
static void OutputJitLog(const asmjit::StringLogger &logger);
|
||||||
|
|
||||||
|
@ -315,6 +316,13 @@ void JitCompiler::SetupSimpleFrame()
|
||||||
cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f)));
|
cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f)));
|
||||||
cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f)));
|
cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f)));
|
||||||
}
|
}
|
||||||
|
else if (type == TypeVector4 || type == TypeFVector4)
|
||||||
|
{
|
||||||
|
cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f)));
|
||||||
|
cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f)));
|
||||||
|
cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f)));
|
||||||
|
cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f)));
|
||||||
|
}
|
||||||
else if (type == TypeFloat64)
|
else if (type == TypeFloat64)
|
||||||
{
|
{
|
||||||
cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f)));
|
cc.movsd(regF[regf++], x86::qword_ptr(args, argsPos++ * sizeof(VMValue) + offsetof(VMValue, f)));
|
||||||
|
@ -551,6 +559,20 @@ asmjit::X86Xmm JitCompiler::CheckRegF(int r0, int r1, int r2, int r3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmjit::X86Xmm JitCompiler::CheckRegF(int r0, int r1, int r2, int r3, int r4)
|
||||||
|
{
|
||||||
|
if (r0 != r1 && r0 != r2 && r0 != r3 && r0 != r4)
|
||||||
|
{
|
||||||
|
return regF[r0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto copy = newTempXmmSd();
|
||||||
|
cc.movsd(copy, regF[r0]);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
asmjit::X86Gp JitCompiler::CheckRegS(int r0, int r1)
|
asmjit::X86Gp JitCompiler::CheckRegS(int r0, int r1)
|
||||||
{
|
{
|
||||||
if (r0 != r1)
|
if (r0 != r1)
|
||||||
|
|
|
@ -182,6 +182,13 @@ int JitCompiler::StoreCallParams()
|
||||||
}
|
}
|
||||||
numparams += 2;
|
numparams += 2;
|
||||||
break;
|
break;
|
||||||
|
case REGT_FLOAT | REGT_MULTIREG4:
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
cc.movsd(x86::qword_ptr(vmframe, offsetParams + (slot + j) * sizeof(VMValue) + myoffsetof(VMValue, f)), regF[bc + j]);
|
||||||
|
}
|
||||||
|
numparams += 3;
|
||||||
|
break;
|
||||||
case REGT_FLOAT | REGT_ADDROF:
|
case REGT_FLOAT | REGT_ADDROF:
|
||||||
cc.lea(stackPtr, x86::ptr(vmframe, offsetF + (int)(bc * sizeof(double))));
|
cc.lea(stackPtr, x86::ptr(vmframe, offsetF + (int)(bc * sizeof(double))));
|
||||||
// When passing the address to a float we don't know if the receiving function will treat it as float, vec2 or vec3.
|
// When passing the address to a float we don't know if the receiving function will treat it as float, vec2 or vec3.
|
||||||
|
@ -256,6 +263,12 @@ void JitCompiler::LoadCallResult(int type, int regnum, bool addrof)
|
||||||
cc.movsd(regF[regnum + 1], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 1) * sizeof(double)));
|
cc.movsd(regF[regnum + 1], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 1) * sizeof(double)));
|
||||||
cc.movsd(regF[regnum + 2], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 2) * sizeof(double)));
|
cc.movsd(regF[regnum + 2], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 2) * sizeof(double)));
|
||||||
}
|
}
|
||||||
|
else if (type & REGT_MULTIREG4)
|
||||||
|
{
|
||||||
|
cc.movsd(regF[regnum + 1], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 1) * sizeof(double)));
|
||||||
|
cc.movsd(regF[regnum + 2], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 2) * sizeof(double)));
|
||||||
|
cc.movsd(regF[regnum + 3], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 3) * sizeof(double)));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case REGT_STRING:
|
case REGT_STRING:
|
||||||
// We don't have to do anything in this case. String values are never moved to virtual registers.
|
// We don't have to do anything in this case. String values are never moved to virtual registers.
|
||||||
|
@ -408,6 +421,11 @@ void JitCompiler::EmitNativeCall(VMNativeFunction *target)
|
||||||
call->setArg(slot + j, regF[bc + j]);
|
call->setArg(slot + j, regF[bc + j]);
|
||||||
numparams += 2;
|
numparams += 2;
|
||||||
break;
|
break;
|
||||||
|
case REGT_FLOAT | REGT_MULTIREG4:
|
||||||
|
for (int j = 0; j < 4; j++)
|
||||||
|
call->setArg(slot + j, regF[bc + j]);
|
||||||
|
numparams += 3;
|
||||||
|
break;
|
||||||
case REGT_FLOAT | REGT_KONST:
|
case REGT_FLOAT | REGT_KONST:
|
||||||
tmp = newTempIntPtr();
|
tmp = newTempIntPtr();
|
||||||
tmp2 = newTempXmmSd();
|
tmp2 = newTempXmmSd();
|
||||||
|
@ -550,6 +568,12 @@ void JitCompiler::EmitNativeCall(VMNativeFunction *target)
|
||||||
cc.movsd(regF[regnum + 1], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 1) * sizeof(double)));
|
cc.movsd(regF[regnum + 1], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 1) * sizeof(double)));
|
||||||
cc.movsd(regF[regnum + 2], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 2) * sizeof(double)));
|
cc.movsd(regF[regnum + 2], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 2) * sizeof(double)));
|
||||||
break;
|
break;
|
||||||
|
case REGT_FLOAT | REGT_MULTIREG4:
|
||||||
|
cc.movsd(regF[regnum], asmjit::x86::qword_ptr(vmframe, offsetF + regnum * sizeof(double)));
|
||||||
|
cc.movsd(regF[regnum + 1], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 1) * sizeof(double)));
|
||||||
|
cc.movsd(regF[regnum + 2], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 2) * sizeof(double)));
|
||||||
|
cc.movsd(regF[regnum + 3], asmjit::x86::qword_ptr(vmframe, offsetF + (regnum + 3) * sizeof(double)));
|
||||||
|
break;
|
||||||
case REGT_STRING:
|
case REGT_STRING:
|
||||||
// We don't have to do anything in this case. String values are never moved to virtual registers.
|
// We don't have to do anything in this case. String values are never moved to virtual registers.
|
||||||
break;
|
break;
|
||||||
|
@ -624,6 +648,13 @@ asmjit::FuncSignature JitCompiler::CreateFuncSignature()
|
||||||
args.Push(TypeIdOf<double>::kTypeId);
|
args.Push(TypeIdOf<double>::kTypeId);
|
||||||
key += "fff";
|
key += "fff";
|
||||||
break;
|
break;
|
||||||
|
case REGT_FLOAT | REGT_MULTIREG4:
|
||||||
|
args.Push(TypeIdOf<double>::kTypeId);
|
||||||
|
args.Push(TypeIdOf<double>::kTypeId);
|
||||||
|
args.Push(TypeIdOf<double>::kTypeId);
|
||||||
|
args.Push(TypeIdOf<double>::kTypeId);
|
||||||
|
key += "ffff";
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
I_Error("Unknown REGT value passed to EmitPARAM\n");
|
I_Error("Unknown REGT value passed to EmitPARAM\n");
|
||||||
|
|
|
@ -110,7 +110,21 @@ void JitCompiler::EmitRET()
|
||||||
if (regtype & REGT_KONST)
|
if (regtype & REGT_KONST)
|
||||||
{
|
{
|
||||||
auto tmp = newTempInt64();
|
auto tmp = newTempInt64();
|
||||||
if (regtype & REGT_MULTIREG3)
|
if (regtype & REGT_MULTIREG4)
|
||||||
|
{
|
||||||
|
cc.mov(tmp, (((int64_t*)konstf)[regnum]));
|
||||||
|
cc.mov(x86::qword_ptr(location), tmp);
|
||||||
|
|
||||||
|
cc.mov(tmp, (((int64_t*)konstf)[regnum + 1]));
|
||||||
|
cc.mov(x86::qword_ptr(location, 8), tmp);
|
||||||
|
|
||||||
|
cc.mov(tmp, (((int64_t*)konstf)[regnum + 2]));
|
||||||
|
cc.mov(x86::qword_ptr(location, 16), tmp);
|
||||||
|
|
||||||
|
cc.mov(tmp, (((int64_t*)konstf)[regnum + 3]));
|
||||||
|
cc.mov(x86::qword_ptr(location, 24), tmp);
|
||||||
|
}
|
||||||
|
else if (regtype & REGT_MULTIREG3)
|
||||||
{
|
{
|
||||||
cc.mov(tmp, (((int64_t *)konstf)[regnum]));
|
cc.mov(tmp, (((int64_t *)konstf)[regnum]));
|
||||||
cc.mov(x86::qword_ptr(location), tmp);
|
cc.mov(x86::qword_ptr(location), tmp);
|
||||||
|
@ -137,7 +151,14 @@ void JitCompiler::EmitRET()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (regtype & REGT_MULTIREG3)
|
if (regtype & REGT_MULTIREG4)
|
||||||
|
{
|
||||||
|
cc.movsd(x86::qword_ptr(location), regF[regnum]);
|
||||||
|
cc.movsd(x86::qword_ptr(location, 8), regF[regnum + 1]);
|
||||||
|
cc.movsd(x86::qword_ptr(location, 16), regF[regnum + 2]);
|
||||||
|
cc.movsd(x86::qword_ptr(location, 24), regF[regnum + 3]);
|
||||||
|
}
|
||||||
|
else if (regtype & REGT_MULTIREG3)
|
||||||
{
|
{
|
||||||
cc.movsd(x86::qword_ptr(location), regF[regnum]);
|
cc.movsd(x86::qword_ptr(location), regF[regnum]);
|
||||||
cc.movsd(x86::qword_ptr(location, 8), regF[regnum + 1]);
|
cc.movsd(x86::qword_ptr(location, 8), regF[regnum + 1]);
|
||||||
|
|
|
@ -325,6 +325,28 @@ void JitCompiler::EmitLV3_R()
|
||||||
cc.movsd(regF[A + 2], asmjit::x86::qword_ptr(tmp, 16));
|
cc.movsd(regF[A + 2], asmjit::x86::qword_ptr(tmp, 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitLV4()
|
||||||
|
{
|
||||||
|
EmitNullPointerThrow(B, X_READ_NIL);
|
||||||
|
auto tmp = newTempIntPtr();
|
||||||
|
cc.lea(tmp, asmjit::x86::qword_ptr(regA[B], konstd[C]));
|
||||||
|
cc.movsd(regF[A], asmjit::x86::qword_ptr(tmp));
|
||||||
|
cc.movsd(regF[A + 1], asmjit::x86::qword_ptr(tmp, 8));
|
||||||
|
cc.movsd(regF[A + 2], asmjit::x86::qword_ptr(tmp, 16));
|
||||||
|
cc.movsd(regF[A + 3], asmjit::x86::qword_ptr(tmp, 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitLV4_R()
|
||||||
|
{
|
||||||
|
EmitNullPointerThrow(B, X_READ_NIL);
|
||||||
|
auto tmp = newTempIntPtr();
|
||||||
|
cc.lea(tmp, asmjit::x86::qword_ptr(regA[B], regD[C]));
|
||||||
|
cc.movsd(regF[A], asmjit::x86::qword_ptr(tmp));
|
||||||
|
cc.movsd(regF[A + 1], asmjit::x86::qword_ptr(tmp, 8));
|
||||||
|
cc.movsd(regF[A + 2], asmjit::x86::qword_ptr(tmp, 16));
|
||||||
|
cc.movsd(regF[A + 3], asmjit::x86::qword_ptr(tmp, 24));
|
||||||
|
}
|
||||||
|
|
||||||
void JitCompiler::EmitLFV2()
|
void JitCompiler::EmitLFV2()
|
||||||
{
|
{
|
||||||
EmitNullPointerThrow(B, X_READ_NIL);
|
EmitNullPointerThrow(B, X_READ_NIL);
|
||||||
|
@ -373,6 +395,36 @@ void JitCompiler::EmitLFV3_R()
|
||||||
cc.cvtss2sd(regF[A + 2], regF[A + 2]);
|
cc.cvtss2sd(regF[A + 2], regF[A + 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitLFV4()
|
||||||
|
{
|
||||||
|
EmitNullPointerThrow(B, X_READ_NIL);
|
||||||
|
auto tmp = newTempIntPtr();
|
||||||
|
cc.lea(tmp, asmjit::x86::qword_ptr(regA[B], konstd[C]));
|
||||||
|
cc.movss(regF[A], asmjit::x86::qword_ptr(tmp));
|
||||||
|
cc.movss(regF[A + 1], asmjit::x86::qword_ptr(tmp, 4));
|
||||||
|
cc.movss(regF[A + 2], asmjit::x86::qword_ptr(tmp, 8));
|
||||||
|
cc.movss(regF[A + 3], asmjit::x86::qword_ptr(tmp, 12));
|
||||||
|
cc.cvtss2sd(regF[A], regF[A]);
|
||||||
|
cc.cvtss2sd(regF[A + 1], regF[A + 1]);
|
||||||
|
cc.cvtss2sd(regF[A + 2], regF[A + 2]);
|
||||||
|
cc.cvtss2sd(regF[A + 3], regF[A + 3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitLFV4_R()
|
||||||
|
{
|
||||||
|
EmitNullPointerThrow(B, X_READ_NIL);
|
||||||
|
auto tmp = newTempIntPtr();
|
||||||
|
cc.lea(tmp, asmjit::x86::qword_ptr(regA[B], regD[C]));
|
||||||
|
cc.movss(regF[A], asmjit::x86::qword_ptr(tmp));
|
||||||
|
cc.movss(regF[A + 1], asmjit::x86::qword_ptr(tmp, 4));
|
||||||
|
cc.movss(regF[A + 2], asmjit::x86::qword_ptr(tmp, 8));
|
||||||
|
cc.movss(regF[A + 3], asmjit::x86::qword_ptr(tmp, 12));
|
||||||
|
cc.cvtss2sd(regF[A], regF[A]);
|
||||||
|
cc.cvtss2sd(regF[A + 1], regF[A + 1]);
|
||||||
|
cc.cvtss2sd(regF[A + 2], regF[A + 2]);
|
||||||
|
cc.cvtss2sd(regF[A + 3], regF[A + 3]);
|
||||||
|
}
|
||||||
|
|
||||||
static void SetString(FString *to, char **from)
|
static void SetString(FString *to, char **from)
|
||||||
{
|
{
|
||||||
*to = *from;
|
*to = *from;
|
||||||
|
|
|
@ -1447,6 +1447,165 @@ void JitCompiler::EmitEQV3_K()
|
||||||
I_Error("EQV3_K is not used.");
|
I_Error("EQV3_K is not used.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Vector math. (4D/Quaternion)
|
||||||
|
|
||||||
|
void JitCompiler::EmitNEGV4()
|
||||||
|
{
|
||||||
|
auto mask = cc.newDoubleConst(asmjit::kConstScopeLocal, -0.0);
|
||||||
|
auto maskXmm = newTempXmmSd();
|
||||||
|
cc.movsd(maskXmm, mask);
|
||||||
|
cc.movsd(regF[A], regF[B]);
|
||||||
|
cc.xorpd(regF[A], maskXmm);
|
||||||
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
||||||
|
cc.xorpd(regF[A + 1], maskXmm);
|
||||||
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
||||||
|
cc.xorpd(regF[A + 2], maskXmm);
|
||||||
|
cc.movsd(regF[A + 3], regF[B + 3]);
|
||||||
|
cc.xorpd(regF[A + 3], maskXmm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitADDV4_RR()
|
||||||
|
{
|
||||||
|
auto rc0 = CheckRegF(C, A);
|
||||||
|
auto rc1 = CheckRegF(C + 1, A + 1);
|
||||||
|
auto rc2 = CheckRegF(C + 2, A + 2);
|
||||||
|
auto rc3 = CheckRegF(C + 3, A + 3);
|
||||||
|
cc.movsd(regF[A], regF[B]);
|
||||||
|
cc.addsd(regF[A], rc0);
|
||||||
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
||||||
|
cc.addsd(regF[A + 1], rc1);
|
||||||
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
||||||
|
cc.addsd(regF[A + 2], rc2);
|
||||||
|
cc.movsd(regF[A + 3], regF[B + 3]);
|
||||||
|
cc.addsd(regF[A + 3], rc3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitSUBV4_RR()
|
||||||
|
{
|
||||||
|
auto rc0 = CheckRegF(C, A);
|
||||||
|
auto rc1 = CheckRegF(C + 1, A + 1);
|
||||||
|
auto rc2 = CheckRegF(C + 2, A + 2);
|
||||||
|
auto rc3 = CheckRegF(C + 3, A + 3);
|
||||||
|
cc.movsd(regF[A], regF[B]);
|
||||||
|
cc.subsd(regF[A], rc0);
|
||||||
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
||||||
|
cc.subsd(regF[A + 1], rc1);
|
||||||
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
||||||
|
cc.subsd(regF[A + 2], rc2);
|
||||||
|
cc.movsd(regF[A + 3], regF[B + 3]);
|
||||||
|
cc.subsd(regF[A + 3], rc3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitDOTV4_RR()
|
||||||
|
{
|
||||||
|
auto rb1 = CheckRegF(B + 1, A);
|
||||||
|
auto rb2 = CheckRegF(B + 2, A);
|
||||||
|
auto rb3 = CheckRegF(B + 3, A);
|
||||||
|
auto rc0 = CheckRegF(C, A);
|
||||||
|
auto rc1 = CheckRegF(C + 1, A);
|
||||||
|
auto rc2 = CheckRegF(C + 2, A);
|
||||||
|
auto rc3 = CheckRegF(C + 3, A);
|
||||||
|
auto tmp = newTempXmmSd();
|
||||||
|
cc.movsd(regF[A], regF[B]);
|
||||||
|
cc.mulsd(regF[A], rc0);
|
||||||
|
cc.movsd(tmp, rb1);
|
||||||
|
cc.mulsd(tmp, rc1);
|
||||||
|
cc.addsd(regF[A], tmp);
|
||||||
|
cc.movsd(tmp, rb2);
|
||||||
|
cc.mulsd(tmp, rc2);
|
||||||
|
cc.addsd(regF[A], tmp);
|
||||||
|
cc.movsd(tmp, rb3);
|
||||||
|
cc.mulsd(tmp, rc3);
|
||||||
|
cc.addsd(regF[A], tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitMULVF4_RR()
|
||||||
|
{
|
||||||
|
auto rc = CheckRegF(C, A, A + 1, A + 2, A + 3);
|
||||||
|
cc.movsd(regF[A], regF[B]);
|
||||||
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
||||||
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
||||||
|
cc.movsd(regF[A + 3], regF[B + 3]);
|
||||||
|
cc.mulsd(regF[A], rc);
|
||||||
|
cc.mulsd(regF[A + 1], rc);
|
||||||
|
cc.mulsd(regF[A + 2], rc);
|
||||||
|
cc.mulsd(regF[A + 3], rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitMULVF4_RK()
|
||||||
|
{
|
||||||
|
auto tmp = newTempIntPtr();
|
||||||
|
cc.movsd(regF[A], regF[B]);
|
||||||
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
||||||
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
||||||
|
cc.movsd(regF[A + 3], regF[B + 3]);
|
||||||
|
cc.mov(tmp, asmjit::imm_ptr(&konstf[C]));
|
||||||
|
cc.mulsd(regF[A], asmjit::x86::qword_ptr(tmp));
|
||||||
|
cc.mulsd(regF[A + 1], asmjit::x86::qword_ptr(tmp));
|
||||||
|
cc.mulsd(regF[A + 2], asmjit::x86::qword_ptr(tmp));
|
||||||
|
cc.mulsd(regF[A + 3], asmjit::x86::qword_ptr(tmp));
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitDIVVF4_RR()
|
||||||
|
{
|
||||||
|
auto rc = CheckRegF(C, A, A + 1, A + 2, A + 3);
|
||||||
|
cc.movsd(regF[A], regF[B]);
|
||||||
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
||||||
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
||||||
|
cc.movsd(regF[A + 3], regF[B + 3]);
|
||||||
|
cc.divsd(regF[A], rc);
|
||||||
|
cc.divsd(regF[A + 1], rc);
|
||||||
|
cc.divsd(regF[A + 2], rc);
|
||||||
|
cc.divsd(regF[A + 3], rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitDIVVF4_RK()
|
||||||
|
{
|
||||||
|
auto tmp = newTempIntPtr();
|
||||||
|
cc.movsd(regF[A], regF[B]);
|
||||||
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
||||||
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
||||||
|
cc.movsd(regF[A + 3], regF[B + 3]);
|
||||||
|
cc.mov(tmp, asmjit::imm_ptr(&konstf[C]));
|
||||||
|
cc.divsd(regF[A], asmjit::x86::qword_ptr(tmp));
|
||||||
|
cc.divsd(regF[A + 1], asmjit::x86::qword_ptr(tmp));
|
||||||
|
cc.divsd(regF[A + 2], asmjit::x86::qword_ptr(tmp));
|
||||||
|
cc.divsd(regF[A + 3], asmjit::x86::qword_ptr(tmp));
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitLENV4()
|
||||||
|
{
|
||||||
|
auto rb1 = CheckRegF(B + 1, A);
|
||||||
|
auto rb2 = CheckRegF(B + 2, A);
|
||||||
|
auto rb3 = CheckRegF(B + 3, A);
|
||||||
|
auto tmp = newTempXmmSd();
|
||||||
|
cc.movsd(regF[A], regF[B]);
|
||||||
|
cc.mulsd(regF[A], regF[B]);
|
||||||
|
cc.movsd(tmp, rb1);
|
||||||
|
cc.mulsd(tmp, rb1);
|
||||||
|
cc.addsd(regF[A], tmp);
|
||||||
|
cc.movsd(tmp, rb2);
|
||||||
|
cc.mulsd(tmp, rb2);
|
||||||
|
cc.addsd(regF[A], tmp);
|
||||||
|
cc.movsd(tmp, rb3);
|
||||||
|
cc.mulsd(tmp, rb3);
|
||||||
|
cc.addsd(regF[A], tmp);
|
||||||
|
CallSqrt(regF[A], regF[A]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitEQV4_R()
|
||||||
|
{
|
||||||
|
EmitComparisonOpcode([&](bool check, asmjit::Label& fail, asmjit::Label& success) {
|
||||||
|
EmitVectorComparison<4> (check, fail, success);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitEQV4_K()
|
||||||
|
{
|
||||||
|
I_Error("EQV4_K is not used.");
|
||||||
|
}
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
// Pointer math.
|
// Pointer math.
|
||||||
|
|
||||||
|
|
|
@ -39,11 +39,20 @@ void JitCompiler::EmitMOVEV3()
|
||||||
cc.movsd(regF[A + 2], regF[B + 2]);
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitMOVEV4()
|
||||||
|
{
|
||||||
|
cc.movsd(regF[A], regF[B]);
|
||||||
|
cc.movsd(regF[A + 1], regF[B + 1]);
|
||||||
|
cc.movsd(regF[A + 2], regF[B + 2]);
|
||||||
|
cc.movsd(regF[A + 3], regF[B + 3]);
|
||||||
|
}
|
||||||
|
|
||||||
static void CastI2S(FString *a, int b) { a->Format("%d", b); }
|
static void CastI2S(FString *a, int b) { a->Format("%d", b); }
|
||||||
static void CastU2S(FString *a, int b) { a->Format("%u", b); }
|
static void CastU2S(FString *a, int b) { a->Format("%u", b); }
|
||||||
static void CastF2S(FString *a, double b) { a->Format("%.5f", b); }
|
static void CastF2S(FString *a, double b) { a->Format("%.5f", b); }
|
||||||
static void CastV22S(FString *a, double b, double b1) { a->Format("(%.5f, %.5f)", b, b1); }
|
static void CastV22S(FString *a, double b, double b1) { a->Format("(%.5f, %.5f)", b, b1); }
|
||||||
static void CastV32S(FString *a, double b, double b1, double b2) { a->Format("(%.5f, %.5f, %.5f)", b, b1, b2); }
|
static void CastV32S(FString *a, double b, double b1, double b2) { a->Format("(%.5f, %.5f, %.5f)", b, b1, b2); }
|
||||||
|
static void CastV42S(FString *a, double b, double b1, double b2, double b3) { a->Format("(%.5f, %.5f, %.5f, %.5f)", b, b1, b2, b3); }
|
||||||
static void CastP2S(FString *a, void *b) { if (b == nullptr) *a = "null"; else a->Format("%p", b); }
|
static void CastP2S(FString *a, void *b) { if (b == nullptr) *a = "null"; else a->Format("%p", b); }
|
||||||
static int CastS2I(FString *b) { return (int)b->ToLong(); }
|
static int CastS2I(FString *b) { return (int)b->ToLong(); }
|
||||||
static double CastS2F(FString *b) { return b->ToDouble(); }
|
static double CastS2F(FString *b) { return b->ToDouble(); }
|
||||||
|
@ -109,6 +118,14 @@ void JitCompiler::EmitCAST()
|
||||||
call->setArg(2, regF[B + 1]);
|
call->setArg(2, regF[B + 1]);
|
||||||
call->setArg(3, regF[B + 2]);
|
call->setArg(3, regF[B + 2]);
|
||||||
break;
|
break;
|
||||||
|
case CAST_V42S:
|
||||||
|
call = CreateCall<void, FString*, double, double, double>(CastV42S);
|
||||||
|
call->setArg(0, regS[A]);
|
||||||
|
call->setArg(1, regF[B]);
|
||||||
|
call->setArg(2, regF[B + 1]);
|
||||||
|
call->setArg(3, regF[B + 2]);
|
||||||
|
call->setArg(4, regF[B + 3]);
|
||||||
|
break;
|
||||||
case CAST_P2S:
|
case CAST_P2S:
|
||||||
call = CreateCall<void, FString*, void*>(CastP2S);
|
call = CreateCall<void, FString*, void*>(CastP2S);
|
||||||
call->setArg(0, regS[A]);
|
call->setArg(0, regS[A]);
|
||||||
|
|
|
@ -161,6 +161,30 @@ void JitCompiler::EmitSV3_R()
|
||||||
cc.movsd(asmjit::x86::qword_ptr(tmp, 16), regF[B + 2]);
|
cc.movsd(asmjit::x86::qword_ptr(tmp, 16), regF[B + 2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitSV4()
|
||||||
|
{
|
||||||
|
EmitNullPointerThrow(A, X_WRITE_NIL);
|
||||||
|
auto tmp = newTempIntPtr();
|
||||||
|
cc.mov(tmp, regA[A]);
|
||||||
|
cc.add(tmp, konstd[C]);
|
||||||
|
cc.movsd(asmjit::x86::qword_ptr(tmp), regF[B]);
|
||||||
|
cc.movsd(asmjit::x86::qword_ptr(tmp, 8), regF[B + 1]);
|
||||||
|
cc.movsd(asmjit::x86::qword_ptr(tmp, 16), regF[B + 2]);
|
||||||
|
cc.movsd(asmjit::x86::qword_ptr(tmp, 24), regF[B + 3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitSV4_R()
|
||||||
|
{
|
||||||
|
EmitNullPointerThrow(A, X_WRITE_NIL);
|
||||||
|
auto tmp = newTempIntPtr();
|
||||||
|
cc.mov(tmp, regA[A]);
|
||||||
|
cc.add(tmp, regD[C]);
|
||||||
|
cc.movsd(asmjit::x86::qword_ptr(tmp), regF[B]);
|
||||||
|
cc.movsd(asmjit::x86::qword_ptr(tmp, 8), regF[B + 1]);
|
||||||
|
cc.movsd(asmjit::x86::qword_ptr(tmp, 16), regF[B + 2]);
|
||||||
|
cc.movsd(asmjit::x86::qword_ptr(tmp, 24), regF[B + 3]);
|
||||||
|
}
|
||||||
|
|
||||||
void JitCompiler::EmitSFV2()
|
void JitCompiler::EmitSFV2()
|
||||||
{
|
{
|
||||||
EmitNullPointerThrow(A, X_WRITE_NIL);
|
EmitNullPointerThrow(A, X_WRITE_NIL);
|
||||||
|
@ -219,6 +243,40 @@ void JitCompiler::EmitSFV3_R()
|
||||||
cc.movss(asmjit::x86::qword_ptr(tmp, 8), tmpF);
|
cc.movss(asmjit::x86::qword_ptr(tmp, 8), tmpF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitSFV4()
|
||||||
|
{
|
||||||
|
EmitNullPointerThrow(A, X_WRITE_NIL);
|
||||||
|
auto tmp = newTempIntPtr();
|
||||||
|
cc.mov(tmp, regA[A]);
|
||||||
|
cc.add(tmp, konstd[C]);
|
||||||
|
auto tmpF = newTempXmmSs();
|
||||||
|
cc.cvtsd2ss(tmpF, regF[B]);
|
||||||
|
cc.movss(asmjit::x86::qword_ptr(tmp), tmpF);
|
||||||
|
cc.cvtsd2ss(tmpF, regF[B + 1]);
|
||||||
|
cc.movss(asmjit::x86::qword_ptr(tmp, 4), tmpF);
|
||||||
|
cc.cvtsd2ss(tmpF, regF[B + 2]);
|
||||||
|
cc.movss(asmjit::x86::qword_ptr(tmp, 8), tmpF);
|
||||||
|
cc.cvtsd2ss(tmpF, regF[B + 3]);
|
||||||
|
cc.movss(asmjit::x86::qword_ptr(tmp, 12), tmpF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JitCompiler::EmitSFV4_R()
|
||||||
|
{
|
||||||
|
EmitNullPointerThrow(A, X_WRITE_NIL);
|
||||||
|
auto tmp = newTempIntPtr();
|
||||||
|
cc.mov(tmp, regA[A]);
|
||||||
|
cc.add(tmp, regD[C]);
|
||||||
|
auto tmpF = newTempXmmSs();
|
||||||
|
cc.cvtsd2ss(tmpF, regF[B]);
|
||||||
|
cc.movss(asmjit::x86::qword_ptr(tmp), tmpF);
|
||||||
|
cc.cvtsd2ss(tmpF, regF[B + 1]);
|
||||||
|
cc.movss(asmjit::x86::qword_ptr(tmp, 4), tmpF);
|
||||||
|
cc.cvtsd2ss(tmpF, regF[B + 2]);
|
||||||
|
cc.movss(asmjit::x86::qword_ptr(tmp, 8), tmpF);
|
||||||
|
cc.cvtsd2ss(tmpF, regF[B + 3]);
|
||||||
|
cc.movss(asmjit::x86::qword_ptr(tmp, 12), tmpF);
|
||||||
|
}
|
||||||
|
|
||||||
void JitCompiler::EmitSBIT()
|
void JitCompiler::EmitSBIT()
|
||||||
{
|
{
|
||||||
EmitNullPointerThrow(A, X_WRITE_NIL);
|
EmitNullPointerThrow(A, X_WRITE_NIL);
|
||||||
|
|
|
@ -241,6 +241,7 @@ private:
|
||||||
asmjit::X86Xmm CheckRegF(int r0, int r1);
|
asmjit::X86Xmm CheckRegF(int r0, int r1);
|
||||||
asmjit::X86Xmm CheckRegF(int r0, int r1, int r2);
|
asmjit::X86Xmm CheckRegF(int r0, int r1, int r2);
|
||||||
asmjit::X86Xmm CheckRegF(int r0, int r1, int r2, int r3);
|
asmjit::X86Xmm CheckRegF(int r0, int r1, int r2, int r3);
|
||||||
|
asmjit::X86Xmm CheckRegF(int r0, int r1, int r2, int r3, int r4);
|
||||||
asmjit::X86Gp CheckRegS(int r0, int r1);
|
asmjit::X86Gp CheckRegS(int r0, int r1);
|
||||||
asmjit::X86Gp CheckRegA(int r0, int r1);
|
asmjit::X86Gp CheckRegA(int r0, int r1);
|
||||||
|
|
||||||
|
|
|
@ -80,8 +80,9 @@ enum
|
||||||
REGT_KONST = 4,
|
REGT_KONST = 4,
|
||||||
REGT_MULTIREG2 = 8,
|
REGT_MULTIREG2 = 8,
|
||||||
REGT_MULTIREG3 = 16, // (e.g. a vector)
|
REGT_MULTIREG3 = 16, // (e.g. a vector)
|
||||||
REGT_MULTIREG = 24,
|
REGT_MULTIREG = 8 | 16 | 64,
|
||||||
REGT_ADDROF = 32, // used with PARAM: pass address of this register
|
REGT_ADDROF = 32, // used with PARAM: pass address of this register
|
||||||
|
REGT_MULTIREG4 = 64,
|
||||||
|
|
||||||
REGT_NIL = 128 // parameter was omitted
|
REGT_NIL = 128 // parameter was omitted
|
||||||
};
|
};
|
||||||
|
@ -130,6 +131,22 @@ struct VMReturn
|
||||||
assert(RegType == REGT_FLOAT);
|
assert(RegType == REGT_FLOAT);
|
||||||
*(double *)Location = val;
|
*(double *)Location = val;
|
||||||
}
|
}
|
||||||
|
void SetVector4(const double val[4])
|
||||||
|
{
|
||||||
|
assert(RegType == (REGT_FLOAT|REGT_MULTIREG4));
|
||||||
|
((double *)Location)[0] = val[0];
|
||||||
|
((double *)Location)[1] = val[1];
|
||||||
|
((double *)Location)[2] = val[2];
|
||||||
|
((double *)Location)[3] = val[3];
|
||||||
|
}
|
||||||
|
void SetVector4(const DVector4 &val)
|
||||||
|
{
|
||||||
|
assert(RegType == (REGT_FLOAT | REGT_MULTIREG4));
|
||||||
|
((double *)Location)[0] = val[0];
|
||||||
|
((double *)Location)[1] = val[1];
|
||||||
|
((double *)Location)[2] = val[2];
|
||||||
|
((double *)Location)[3] = val[3];
|
||||||
|
}
|
||||||
void SetVector(const double val[3])
|
void SetVector(const double val[3])
|
||||||
{
|
{
|
||||||
assert(RegType == (REGT_FLOAT|REGT_MULTIREG3));
|
assert(RegType == (REGT_FLOAT|REGT_MULTIREG3));
|
||||||
|
|
|
@ -287,18 +287,40 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret)
|
||||||
{
|
{
|
||||||
auto v = (double*)ptr;
|
auto v = (double*)ptr;
|
||||||
reg.f[a] = v[0];
|
reg.f[a] = v[0];
|
||||||
reg.f[a + 1] = v[1];
|
reg.f[a+1] = v[1];
|
||||||
reg.f[a + 2] = v[2];
|
reg.f[a+2] = v[2];
|
||||||
}
|
}
|
||||||
NEXTOP;
|
NEXTOP;
|
||||||
OP(LV3_R) :
|
OP(LV3_R) :
|
||||||
ASSERTF(a + 2); ASSERTA(B); ASSERTD(C);
|
ASSERTF(a + 2); ASSERTA(B); ASSERTD(C);
|
||||||
GETADDR(PB, RC, X_READ_NIL);
|
GETADDR(PB, RC, X_READ_NIL);
|
||||||
|
{
|
||||||
|
auto v = (double*)ptr;
|
||||||
|
reg.f[a] = v[0];
|
||||||
|
reg.f[a+1] = v[1];
|
||||||
|
reg.f[a+2] = v[2];
|
||||||
|
}
|
||||||
|
NEXTOP;
|
||||||
|
OP(LV4) :
|
||||||
|
ASSERTF(a + 3); ASSERTA(B); ASSERTKD(C);
|
||||||
|
GETADDR(PB, KC, X_READ_NIL);
|
||||||
{
|
{
|
||||||
auto v = (double*)ptr;
|
auto v = (double*)ptr;
|
||||||
reg.f[a] = v[0];
|
reg.f[a] = v[0];
|
||||||
reg.f[a + 1] = v[1];
|
reg.f[a + 1] = v[1];
|
||||||
reg.f[a + 2] = v[2];
|
reg.f[a + 2] = v[2];
|
||||||
|
reg.f[a + 3] = v[3];
|
||||||
|
}
|
||||||
|
NEXTOP;
|
||||||
|
OP(LV4_R) :
|
||||||
|
ASSERTF(a + 3); ASSERTA(B); ASSERTD(C);
|
||||||
|
GETADDR(PB, RC, X_READ_NIL);
|
||||||
|
{
|
||||||
|
auto v = (double*)ptr;
|
||||||
|
reg.f[a] = v[0];
|
||||||
|
reg.f[a + 1] = v[1];
|
||||||
|
reg.f[a + 2] = v[2];
|
||||||
|
reg.f[a + 3] = v[3];
|
||||||
}
|
}
|
||||||
NEXTOP;
|
NEXTOP;
|
||||||
OP(LFV2):
|
OP(LFV2):
|
||||||
|
@ -339,6 +361,28 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret)
|
||||||
reg.f[a+2] = v[2];
|
reg.f[a+2] = v[2];
|
||||||
}
|
}
|
||||||
NEXTOP;
|
NEXTOP;
|
||||||
|
OP(LFV4) :
|
||||||
|
ASSERTF(a + 3); ASSERTA(B); ASSERTKD(C);
|
||||||
|
GETADDR(PB, KC, X_READ_NIL);
|
||||||
|
{
|
||||||
|
auto v = (float*)ptr;
|
||||||
|
reg.f[a] = v[0];
|
||||||
|
reg.f[a+1] = v[1];
|
||||||
|
reg.f[a+2] = v[2];
|
||||||
|
reg.f[a+3] = v[3];
|
||||||
|
}
|
||||||
|
NEXTOP;
|
||||||
|
OP(LFV4_R) :
|
||||||
|
ASSERTF(a + 3); ASSERTA(B); ASSERTD(C);
|
||||||
|
GETADDR(PB, RC, X_READ_NIL);
|
||||||
|
{
|
||||||
|
auto v = (float*)ptr;
|
||||||
|
reg.f[a] = v[0];
|
||||||
|
reg.f[a+1] = v[1];
|
||||||
|
reg.f[a+2] = v[2];
|
||||||
|
reg.f[a+3] = v[3];
|
||||||
|
}
|
||||||
|
NEXTOP;
|
||||||
OP(LBIT):
|
OP(LBIT):
|
||||||
ASSERTD(a); ASSERTA(B);
|
ASSERTD(a); ASSERTA(B);
|
||||||
GETADDR(PB,0,X_READ_NIL);
|
GETADDR(PB,0,X_READ_NIL);
|
||||||
|
@ -468,6 +512,28 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret)
|
||||||
v[2] = reg.f[B+2];
|
v[2] = reg.f[B+2];
|
||||||
}
|
}
|
||||||
NEXTOP;
|
NEXTOP;
|
||||||
|
OP(SV4):
|
||||||
|
ASSERTA(a); ASSERTF(B+3); ASSERTKD(C);
|
||||||
|
GETADDR(PA,KC,X_WRITE_NIL);
|
||||||
|
{
|
||||||
|
auto v = (double *)ptr;
|
||||||
|
v[0] = reg.f[B];
|
||||||
|
v[1] = reg.f[B+1];
|
||||||
|
v[2] = reg.f[B+2];
|
||||||
|
v[3] = reg.f[B+3];
|
||||||
|
}
|
||||||
|
NEXTOP;
|
||||||
|
OP(SV4_R):
|
||||||
|
ASSERTA(a); ASSERTF(B+3); ASSERTD(C);
|
||||||
|
GETADDR(PA,RC,X_WRITE_NIL);
|
||||||
|
{
|
||||||
|
auto v = (double *)ptr;
|
||||||
|
v[0] = reg.f[B];
|
||||||
|
v[1] = reg.f[B+1];
|
||||||
|
v[2] = reg.f[B+2];
|
||||||
|
v[3] = reg.f[B+3];
|
||||||
|
}
|
||||||
|
NEXTOP;
|
||||||
OP(SFV2):
|
OP(SFV2):
|
||||||
ASSERTA(a); ASSERTF(B+1); ASSERTKD(C);
|
ASSERTA(a); ASSERTF(B+1); ASSERTKD(C);
|
||||||
GETADDR(PA,KC,X_WRITE_NIL);
|
GETADDR(PA,KC,X_WRITE_NIL);
|
||||||
|
@ -506,6 +572,28 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret)
|
||||||
v[2] = (float)reg.f[B+2];
|
v[2] = (float)reg.f[B+2];
|
||||||
}
|
}
|
||||||
NEXTOP;
|
NEXTOP;
|
||||||
|
OP(SFV4):
|
||||||
|
ASSERTA(a); ASSERTF(B+3); ASSERTKD(C);
|
||||||
|
GETADDR(PA,KC,X_WRITE_NIL);
|
||||||
|
{
|
||||||
|
auto v = (float *)ptr;
|
||||||
|
v[0] = (float)reg.f[B];
|
||||||
|
v[1] = (float)reg.f[B+1];
|
||||||
|
v[2] = (float)reg.f[B+2];
|
||||||
|
v[3] = (float)reg.f[B+3];
|
||||||
|
}
|
||||||
|
NEXTOP;
|
||||||
|
OP(SFV4_R):
|
||||||
|
ASSERTA(a); ASSERTF(B+3); ASSERTD(C);
|
||||||
|
GETADDR(PA,RC,X_WRITE_NIL);
|
||||||
|
{
|
||||||
|
auto v = (float *)ptr;
|
||||||
|
v[0] = (float)reg.f[B];
|
||||||
|
v[1] = (float)reg.f[B+1];
|
||||||
|
v[2] = (float)reg.f[B+2];
|
||||||
|
v[3] = (float)reg.f[B+3];
|
||||||
|
}
|
||||||
|
NEXTOP;
|
||||||
OP(SBIT):
|
OP(SBIT):
|
||||||
ASSERTA(a); ASSERTD(B);
|
ASSERTA(a); ASSERTD(B);
|
||||||
GETADDR(PA,0,X_WRITE_NIL);
|
GETADDR(PA,0,X_WRITE_NIL);
|
||||||
|
@ -555,6 +643,16 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret)
|
||||||
reg.f[a + 2] = reg.f[b + 2];
|
reg.f[a + 2] = reg.f[b + 2];
|
||||||
NEXTOP;
|
NEXTOP;
|
||||||
}
|
}
|
||||||
|
OP(MOVEV4) :
|
||||||
|
{
|
||||||
|
ASSERTF(a); ASSERTF(B);
|
||||||
|
b = B;
|
||||||
|
reg.f[a] = reg.f[b];
|
||||||
|
reg.f[a + 1] = reg.f[b + 1];
|
||||||
|
reg.f[a + 2] = reg.f[b + 2];
|
||||||
|
reg.f[a + 3] = reg.f[b + 3];
|
||||||
|
NEXTOP;
|
||||||
|
}
|
||||||
OP(DYNCAST_R) :
|
OP(DYNCAST_R) :
|
||||||
ASSERTA(a); ASSERTA(B); ASSERTA(C);
|
ASSERTA(a); ASSERTA(B); ASSERTA(C);
|
||||||
b = B;
|
b = B;
|
||||||
|
@ -713,6 +811,15 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret)
|
||||||
::new(param + 2) VMValue(reg.f[b + 2]);
|
::new(param + 2) VMValue(reg.f[b + 2]);
|
||||||
f->NumParam += 2;
|
f->NumParam += 2;
|
||||||
break;
|
break;
|
||||||
|
case REGT_FLOAT | REGT_MULTIREG4:
|
||||||
|
assert(b < f->NumRegF - 3);
|
||||||
|
assert(f->NumParam < sfunc->MaxParam - 2);
|
||||||
|
::new(param) VMValue(reg.f[b]);
|
||||||
|
::new(param + 1) VMValue(reg.f[b + 1]);
|
||||||
|
::new(param + 2) VMValue(reg.f[b + 2]);
|
||||||
|
::new(param + 3) VMValue(reg.f[b + 3]);
|
||||||
|
f->NumParam += 3;
|
||||||
|
break;
|
||||||
case REGT_FLOAT | REGT_ADDROF:
|
case REGT_FLOAT | REGT_ADDROF:
|
||||||
assert(b < f->NumRegF);
|
assert(b < f->NumRegF);
|
||||||
::new(param) VMValue(®.f[b]);
|
::new(param) VMValue(®.f[b]);
|
||||||
|
@ -1690,6 +1797,97 @@ static int ExecScriptFunc(VMFrameStack *stack, VMReturn *ret, int numret)
|
||||||
fcp = &konstf[C];
|
fcp = &konstf[C];
|
||||||
goto Do_EQV3;
|
goto Do_EQV3;
|
||||||
|
|
||||||
|
OP(NEGV4):
|
||||||
|
ASSERTF(a+3); ASSERTF(B+3);
|
||||||
|
reg.f[a] = -reg.f[B];
|
||||||
|
reg.f[a+1] = -reg.f[B+1];
|
||||||
|
reg.f[a+2] = -reg.f[B+2];
|
||||||
|
reg.f[a+3] = -reg.f[B+3];
|
||||||
|
NEXTOP;
|
||||||
|
|
||||||
|
OP(ADDV4_RR):
|
||||||
|
ASSERTF(a+3); ASSERTF(B+3); ASSERTF(C+3);
|
||||||
|
fcp = ®.f[C];
|
||||||
|
fbp = ®.f[B];
|
||||||
|
reg.f[a] = fbp[0] + fcp[0];
|
||||||
|
reg.f[a+1] = fbp[1] + fcp[1];
|
||||||
|
reg.f[a+2] = fbp[2] + fcp[2];
|
||||||
|
reg.f[a+3] = fbp[3] + fcp[3];
|
||||||
|
NEXTOP;
|
||||||
|
|
||||||
|
OP(SUBV4_RR):
|
||||||
|
ASSERTF(a+3); ASSERTF(B+3); ASSERTF(C+3);
|
||||||
|
fbp = ®.f[B];
|
||||||
|
fcp = ®.f[C];
|
||||||
|
reg.f[a] = fbp[0] - fcp[0];
|
||||||
|
reg.f[a+1] = fbp[1] - fcp[1];
|
||||||
|
reg.f[a+2] = fbp[2] - fcp[2];
|
||||||
|
reg.f[a+3] = fbp[3] - fcp[3];
|
||||||
|
NEXTOP;
|
||||||
|
|
||||||
|
OP(DOTV4_RR):
|
||||||
|
ASSERTF(a); ASSERTF(B+3); ASSERTF(C+3);
|
||||||
|
reg.f[a] = reg.f[B] * reg.f[C] + reg.f[B+1] * reg.f[C+1] + reg.f[B+2] * reg.f[C+2] + reg.f[B+3] * reg.f[C+3];
|
||||||
|
NEXTOP;
|
||||||
|
|
||||||
|
OP(MULVF4_RR):
|
||||||
|
ASSERTF(a+3); ASSERTF(B+3); ASSERTF(C);
|
||||||
|
fc = reg.f[C];
|
||||||
|
fbp = ®.f[B];
|
||||||
|
Do_MULV4:
|
||||||
|
reg.f[a] = fbp[0] * fc;
|
||||||
|
reg.f[a+1] = fbp[1] * fc;
|
||||||
|
reg.f[a+2] = fbp[2] * fc;
|
||||||
|
reg.f[a+3] = fbp[3] * fc;
|
||||||
|
NEXTOP;
|
||||||
|
OP(MULVF4_RK):
|
||||||
|
ASSERTF(a+3); ASSERTF(B+3); ASSERTKF(C);
|
||||||
|
fc = konstf[C];
|
||||||
|
fbp = ®.f[B];
|
||||||
|
goto Do_MULV4;
|
||||||
|
|
||||||
|
OP(DIVVF4_RR):
|
||||||
|
ASSERTF(a+3); ASSERTF(B+3); ASSERTF(C);
|
||||||
|
fc = reg.f[C];
|
||||||
|
fbp = ®.f[B];
|
||||||
|
Do_DIVV4:
|
||||||
|
reg.f[a] = fbp[0] / fc;
|
||||||
|
reg.f[a+1] = fbp[1] / fc;
|
||||||
|
reg.f[a+2] = fbp[2] / fc;
|
||||||
|
reg.f[a+3] = fbp[3] / fc;
|
||||||
|
NEXTOP;
|
||||||
|
OP(DIVVF4_RK):
|
||||||
|
ASSERTF(a+3); ASSERTF(B+3); ASSERTKF(C);
|
||||||
|
fc = konstf[C];
|
||||||
|
fbp = ®.f[B];
|
||||||
|
goto Do_DIVV4;
|
||||||
|
|
||||||
|
OP(LENV4):
|
||||||
|
ASSERTF(a); ASSERTF(B+3);
|
||||||
|
reg.f[a] = g_sqrt(reg.f[B] * reg.f[B] + reg.f[B+1] * reg.f[B+1] + reg.f[B+2] * reg.f[B+2]+ reg.f[B+3] * reg.f[B+3]);
|
||||||
|
NEXTOP;
|
||||||
|
|
||||||
|
OP(EQV4_R):
|
||||||
|
ASSERTF(B+3); ASSERTF(C+3);
|
||||||
|
fcp = ®.f[C];
|
||||||
|
Do_EQV4:
|
||||||
|
if (a & CMP_APPROX)
|
||||||
|
{
|
||||||
|
CMPJMP(fabs(reg.f[B ] - fcp[0]) < VM_EPSILON &&
|
||||||
|
fabs(reg.f[B+1] - fcp[1]) < VM_EPSILON &&
|
||||||
|
fabs(reg.f[B+2] - fcp[2]) < VM_EPSILON &&
|
||||||
|
fabs(reg.f[B+3] - fcp[3]) < VM_EPSILON);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CMPJMP(reg.f[B] == fcp[0] && reg.f[B+1] == fcp[1] && reg.f[B+2] == fcp[2] && reg.f[B+3] == fcp[3]);
|
||||||
|
}
|
||||||
|
NEXTOP;
|
||||||
|
OP(EQV4_K):
|
||||||
|
ASSERTF(B+3); ASSERTKF(C+3);
|
||||||
|
fcp = &konstf[C];
|
||||||
|
goto Do_EQV4;
|
||||||
|
|
||||||
OP(ADDA_RR):
|
OP(ADDA_RR):
|
||||||
ASSERTA(a); ASSERTA(B); ASSERTD(C);
|
ASSERTA(a); ASSERTA(B); ASSERTD(C);
|
||||||
c = reg.d[C];
|
c = reg.d[C];
|
||||||
|
@ -2028,7 +2226,11 @@ static void SetReturn(const VMRegisters ®, VMFrame *frame, VMReturn *ret, VM_
|
||||||
assert(regnum < frame->NumRegF);
|
assert(regnum < frame->NumRegF);
|
||||||
src = ®.f[regnum];
|
src = ®.f[regnum];
|
||||||
}
|
}
|
||||||
if (regtype & REGT_MULTIREG3)
|
if (regtype & REGT_MULTIREG4)
|
||||||
|
{
|
||||||
|
ret->SetVector4((double*)src);
|
||||||
|
}
|
||||||
|
else if (regtype & REGT_MULTIREG3)
|
||||||
{
|
{
|
||||||
ret->SetVector((double *)src);
|
ret->SetVector((double *)src);
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,6 +126,7 @@ enum
|
||||||
CAST_So2S,
|
CAST_So2S,
|
||||||
CAST_V22S,
|
CAST_V22S,
|
||||||
CAST_V32S,
|
CAST_V32S,
|
||||||
|
CAST_V42S,
|
||||||
CAST_SID2S,
|
CAST_SID2S,
|
||||||
CAST_TID2S,
|
CAST_TID2S,
|
||||||
|
|
||||||
|
|
|
@ -51,12 +51,16 @@ xx(LV2, lv2, RVRPKI, LV2_R, 4, REGT_INT) // load vector2
|
||||||
xx(LV2_R, lv2, RVRPRI, NOP, 0, 0)
|
xx(LV2_R, lv2, RVRPRI, NOP, 0, 0)
|
||||||
xx(LV3, lv3, RVRPKI, LV3_R, 4, REGT_INT) // load vector3
|
xx(LV3, lv3, RVRPKI, LV3_R, 4, REGT_INT) // load vector3
|
||||||
xx(LV3_R, lv3, RVRPRI, NOP, 0, 0)
|
xx(LV3_R, lv3, RVRPRI, NOP, 0, 0)
|
||||||
|
xx(LV4, lv4, RVRPKI, LV4_R, 4, REGT_INT) // load vector4
|
||||||
|
xx(LV4_R, lv4, RVRPRI, NOP, 0, 0)
|
||||||
xx(LCS, lcs, RSRPKI, LCS_R, 4, REGT_INT) // load string from char ptr.
|
xx(LCS, lcs, RSRPKI, LCS_R, 4, REGT_INT) // load string from char ptr.
|
||||||
xx(LCS_R, lcs, RSRPRI, NOP, 0, 0)
|
xx(LCS_R, lcs, RSRPRI, NOP, 0, 0)
|
||||||
xx(LFV2, lfv2, RVRPKI, LFV2_R, 4, REGT_INT) // load fvector2
|
xx(LFV2, lfv2, RVRPKI, LFV2_R, 4, REGT_INT) // load fvector2
|
||||||
xx(LFV2_R, lfv2, RVRPRI, NOP, 0, 0)
|
xx(LFV2_R, lfv2, RVRPRI, NOP, 0, 0)
|
||||||
xx(LFV3, lfv3, RVRPKI, LFV3_R, 4, REGT_INT) // load fvector3
|
xx(LFV3, lfv3, RVRPKI, LFV3_R, 4, REGT_INT) // load fvector3
|
||||||
xx(LFV3_R, lfv3, RVRPRI, NOP, 0, 0)
|
xx(LFV3_R, lfv3, RVRPRI, NOP, 0, 0)
|
||||||
|
xx(LFV4, lfv4, RVRPKI, LFV4_R, 4, REGT_INT) // load fvector4
|
||||||
|
xx(LFV4_R, lfv4, RVRPRI, NOP, 0, 0)
|
||||||
|
|
||||||
xx(LBIT, lbit, RIRPI8, NOP, 0, 0) // rA = !!(*rB & C) -- *rB is a byte
|
xx(LBIT, lbit, RIRPI8, NOP, 0, 0) // rA = !!(*rB & C) -- *rB is a byte
|
||||||
|
|
||||||
|
@ -81,10 +85,14 @@ xx(SV2, sv2, RPRVKI, SV2_R, 4, REGT_INT) // store vector2
|
||||||
xx(SV2_R, sv2, RPRVRI, NOP, 0, 0)
|
xx(SV2_R, sv2, RPRVRI, NOP, 0, 0)
|
||||||
xx(SV3, sv3, RPRVKI, SV3_R, 4, REGT_INT) // store vector3
|
xx(SV3, sv3, RPRVKI, SV3_R, 4, REGT_INT) // store vector3
|
||||||
xx(SV3_R, sv3, RPRVRI, NOP, 0, 0)
|
xx(SV3_R, sv3, RPRVRI, NOP, 0, 0)
|
||||||
|
xx(SV4, sv4, RPRVKI, SV4_R, 4, REGT_INT) // store vector4
|
||||||
|
xx(SV4_R, sv4, RPRVRI, NOP, 0, 0)
|
||||||
xx(SFV2, sfv2, RPRVKI, SFV2_R, 4, REGT_INT) // store fvector2
|
xx(SFV2, sfv2, RPRVKI, SFV2_R, 4, REGT_INT) // store fvector2
|
||||||
xx(SFV2_R, sfv2, RPRVRI, NOP, 0, 0)
|
xx(SFV2_R, sfv2, RPRVRI, NOP, 0, 0)
|
||||||
xx(SFV3, sfv3, RPRVKI, SFV3_R, 4, REGT_INT) // store fvector3
|
xx(SFV3, sfv3, RPRVKI, SFV3_R, 4, REGT_INT) // store fvector3
|
||||||
xx(SFV3_R, sfv3, RPRVRI, NOP, 0, 0)
|
xx(SFV3_R, sfv3, RPRVRI, NOP, 0, 0)
|
||||||
|
xx(SFV4, sfv4, RPRVKI, SFV4_R, 4, REGT_INT) // store fvector4
|
||||||
|
xx(SFV4_R, sfv4, RPRVRI, NOP, 0, 0)
|
||||||
|
|
||||||
xx(SBIT, sbit, RPRII8, NOP, 0, 0) // *rA |= C if rB is true, *rA &= ~C otherwise
|
xx(SBIT, sbit, RPRII8, NOP, 0, 0) // *rA |= C if rB is true, *rA &= ~C otherwise
|
||||||
|
|
||||||
|
@ -95,6 +103,7 @@ xx(MOVES, mov, RSRS, NOP, 0, 0) // sA = sB
|
||||||
xx(MOVEA, mov, RPRP, NOP, 0, 0) // aA = aB
|
xx(MOVEA, mov, RPRP, NOP, 0, 0) // aA = aB
|
||||||
xx(MOVEV2, mov2, RFRF, NOP, 0, 0) // fA = fB (2 elements)
|
xx(MOVEV2, mov2, RFRF, NOP, 0, 0) // fA = fB (2 elements)
|
||||||
xx(MOVEV3, mov3, RFRF, NOP, 0, 0) // fA = fB (3 elements)
|
xx(MOVEV3, mov3, RFRF, NOP, 0, 0) // fA = fB (3 elements)
|
||||||
|
xx(MOVEV4, mov4, RFRF, NOP, 0, 0) // fA = fB (4 elements)
|
||||||
xx(CAST, cast, CAST, NOP, 0, 0) // xA = xB, conversion specified by C
|
xx(CAST, cast, CAST, NOP, 0, 0) // xA = xB, conversion specified by C
|
||||||
xx(CASTB, castb, CAST, NOP, 0, 0) // xA = !!xB, type specified by C
|
xx(CASTB, castb, CAST, NOP, 0, 0) // xA = !!xB, type specified by C
|
||||||
xx(DYNCAST_R, dyncast, RPRPRP, NOP, 0, 0) // aA = dyn_cast<aC>(aB);
|
xx(DYNCAST_R, dyncast, RPRPRP, NOP, 0, 0) // aA = dyn_cast<aC>(aB);
|
||||||
|
@ -256,6 +265,19 @@ xx(LENV3, lenv3, RFRV, NOP, 0, 0) // fA = vB.Length
|
||||||
xx(EQV3_R, beqv3, CVRR, NOP, 0, 0) // if ((vB == vkC) != A) then pc++ (inexact if A & 33)
|
xx(EQV3_R, beqv3, CVRR, NOP, 0, 0) // if ((vB == vkC) != A) then pc++ (inexact if A & 33)
|
||||||
xx(EQV3_K, beqv3, CVRK, NOP, 0, 0) // this will never be used.
|
xx(EQV3_K, beqv3, CVRK, NOP, 0, 0) // this will never be used.
|
||||||
|
|
||||||
|
// Vector math (4D)
|
||||||
|
xx(NEGV4, negv4, RVRV, NOP, 0, 0) // vA = -vB
|
||||||
|
xx(ADDV4_RR, addv4, RVRVRV, NOP, 0, 0) // vA = vB + vkC
|
||||||
|
xx(SUBV4_RR, subv4, RVRVRV, NOP, 0, 0) // vA = vkB - vkC
|
||||||
|
xx(DOTV4_RR, dotv4, RVRVRV, NOP, 0, 0) // va = vB dot vkC
|
||||||
|
xx(MULVF4_RR, mulv4, RVRVRF, NOP, 0, 0) // vA = vkB * fkC
|
||||||
|
xx(MULVF4_RK, mulv4, RVRVKF, MULVF4_RR, 4, REGT_FLOAT)
|
||||||
|
xx(DIVVF4_RR, divv4, RVRVRF, NOP, 0, 0) // vA = vkB / fkC
|
||||||
|
xx(DIVVF4_RK, divv4, RVRVKF, DIVVF4_RR, 4, REGT_FLOAT)
|
||||||
|
xx(LENV4, lenv4, RFRV, NOP, 0, 0) // fA = vB.Length
|
||||||
|
xx(EQV4_R, beqv4, CVRR, NOP, 0, 0) // if ((vB == vkC) != A) then pc++ (inexact if A & 33)
|
||||||
|
xx(EQV4_K, beqv4, CVRK, NOP, 0, 0) // this will never be used.
|
||||||
|
|
||||||
// Pointer math.
|
// Pointer math.
|
||||||
xx(ADDA_RR, add, RPRPRI, NOP, 0, 0) // pA = pB + dkC
|
xx(ADDA_RR, add, RPRPRI, NOP, 0, 0) // pA = pB + dkC
|
||||||
xx(ADDA_RK, add, RPRPKI, ADDA_RR,4, REGT_INT)
|
xx(ADDA_RK, add, RPRPKI, ADDA_RR,4, REGT_INT)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
** Wrappers for the malloc family of functions that count used bytes.
|
** Wrappers for the malloc family of functions that count used bytes.
|
||||||
**
|
**
|
||||||
**---------------------------------------------------------------------------
|
**---------------------------------------------------------------------------
|
||||||
** Copyright 1998-2008 Randy Heit
|
** Copyright 1998-2008 Marisa Heit
|
||||||
** All rights reserved.
|
** All rights reserved.
|
||||||
**
|
**
|
||||||
** Redistribution and use in source and binary forms, with or without
|
** Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "engineerrors.h"
|
#include "engineerrors.h"
|
||||||
#include "dobject.h"
|
#include "dobjgc.h"
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
#define _NORMAL_BLOCK 0
|
#define _NORMAL_BLOCK 0
|
||||||
|
@ -59,25 +59,22 @@ void *M_Malloc(size_t size)
|
||||||
{
|
{
|
||||||
void *block = malloc(size);
|
void *block = malloc(size);
|
||||||
|
|
||||||
if (block == NULL)
|
if (block == nullptr)
|
||||||
I_FatalError("Could not malloc %zu bytes", size);
|
I_FatalError("Could not malloc %zu bytes", size);
|
||||||
|
|
||||||
GC::AllocBytes += _msize(block);
|
GC::ReportAlloc(_msize(block));
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *M_Realloc(void *memblock, size_t size)
|
void *M_Realloc(void *memblock, size_t size)
|
||||||
{
|
{
|
||||||
if (memblock != NULL)
|
size_t oldsize = memblock ? _msize(memblock) : 0;
|
||||||
{
|
|
||||||
GC::AllocBytes -= _msize(memblock);
|
|
||||||
}
|
|
||||||
void *block = realloc(memblock, size);
|
void *block = realloc(memblock, size);
|
||||||
if (block == NULL)
|
if (block == nullptr)
|
||||||
{
|
{
|
||||||
I_FatalError("Could not realloc %zu bytes", size);
|
I_FatalError("Could not realloc %zu bytes", size);
|
||||||
}
|
}
|
||||||
GC::AllocBytes += _msize(block);
|
GC::ReportRealloc(oldsize, _msize(block));
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -85,28 +82,25 @@ void *M_Malloc(size_t size)
|
||||||
{
|
{
|
||||||
void *block = malloc(size+sizeof(size_t));
|
void *block = malloc(size+sizeof(size_t));
|
||||||
|
|
||||||
if (block == NULL)
|
if (block == nullptr)
|
||||||
I_FatalError("Could not malloc %zu bytes", size);
|
I_FatalError("Could not malloc %zu bytes", size);
|
||||||
|
|
||||||
size_t *sizeStore = (size_t *) block;
|
size_t *sizeStore = (size_t *) block;
|
||||||
*sizeStore = size;
|
*sizeStore = size;
|
||||||
block = sizeStore+1;
|
block = sizeStore+1;
|
||||||
|
|
||||||
GC::AllocBytes += _msize(block);
|
GC::ReportAlloc(_msize(block));
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *M_Realloc(void *memblock, size_t size)
|
void *M_Realloc(void *memblock, size_t size)
|
||||||
{
|
{
|
||||||
if(memblock == NULL)
|
if (memblock == nullptr)
|
||||||
return M_Malloc(size);
|
return M_Malloc(size);
|
||||||
|
|
||||||
if (memblock != NULL)
|
size_t oldsize = _msize(memblock);
|
||||||
{
|
|
||||||
GC::AllocBytes -= _msize(memblock);
|
|
||||||
}
|
|
||||||
void *block = realloc(((size_t*) memblock)-1, size+sizeof(size_t));
|
void *block = realloc(((size_t*) memblock)-1, size+sizeof(size_t));
|
||||||
if (block == NULL)
|
if (block == nullptr)
|
||||||
{
|
{
|
||||||
I_FatalError("Could not realloc %zu bytes", size);
|
I_FatalError("Could not realloc %zu bytes", size);
|
||||||
}
|
}
|
||||||
|
@ -115,7 +109,7 @@ void *M_Realloc(void *memblock, size_t size)
|
||||||
*sizeStore = size;
|
*sizeStore = size;
|
||||||
block = sizeStore+1;
|
block = sizeStore+1;
|
||||||
|
|
||||||
GC::AllocBytes += _msize(block);
|
GC::ReportRealloc(oldsize, _msize(block));
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -129,25 +123,22 @@ void *M_Malloc_Dbg(size_t size, const char *file, int lineno)
|
||||||
{
|
{
|
||||||
void *block = _malloc_dbg(size, _NORMAL_BLOCK, file, lineno);
|
void *block = _malloc_dbg(size, _NORMAL_BLOCK, file, lineno);
|
||||||
|
|
||||||
if (block == NULL)
|
if (block == nullptr)
|
||||||
I_FatalError("Could not malloc %zu bytes in %s, line %d", size, file, lineno);
|
I_FatalError("Could not malloc %zu bytes in %s, line %d", size, file, lineno);
|
||||||
|
|
||||||
GC::AllocBytes += _msize(block);
|
GC::ReportAlloc(_msize(block));
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno)
|
void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno)
|
||||||
{
|
{
|
||||||
if (memblock != NULL)
|
size_t oldsize = memblock ? _msize(memblock) : 0;
|
||||||
{
|
|
||||||
GC::AllocBytes -= _msize(memblock);
|
|
||||||
}
|
|
||||||
void *block = _realloc_dbg(memblock, size, _NORMAL_BLOCK, file, lineno);
|
void *block = _realloc_dbg(memblock, size, _NORMAL_BLOCK, file, lineno);
|
||||||
if (block == NULL)
|
if (block == nullptr)
|
||||||
{
|
{
|
||||||
I_FatalError("Could not realloc %zu bytes in %s, line %d", size, file, lineno);
|
I_FatalError("Could not realloc %zu bytes in %s, line %d", size, file, lineno);
|
||||||
}
|
}
|
||||||
GC::AllocBytes += _msize(block);
|
GC::ReportRealloc(oldsize, _msize(block));
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -155,29 +146,26 @@ void *M_Malloc_Dbg(size_t size, const char *file, int lineno)
|
||||||
{
|
{
|
||||||
void *block = _malloc_dbg(size+sizeof(size_t), _NORMAL_BLOCK, file, lineno);
|
void *block = _malloc_dbg(size+sizeof(size_t), _NORMAL_BLOCK, file, lineno);
|
||||||
|
|
||||||
if (block == NULL)
|
if (block == nullptr)
|
||||||
I_FatalError("Could not malloc %zu bytes in %s, line %d", size, file, lineno);
|
I_FatalError("Could not malloc %zu bytes in %s, line %d", size, file, lineno);
|
||||||
|
|
||||||
size_t *sizeStore = (size_t *) block;
|
size_t *sizeStore = (size_t *) block;
|
||||||
*sizeStore = size;
|
*sizeStore = size;
|
||||||
block = sizeStore+1;
|
block = sizeStore+1;
|
||||||
|
|
||||||
GC::AllocBytes += _msize(block);
|
GC::ReportAlloc(_msize(block));
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno)
|
void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno)
|
||||||
{
|
{
|
||||||
if(memblock == NULL)
|
if (memblock == nullptr)
|
||||||
return M_Malloc_Dbg(size, file, lineno);
|
return M_Malloc_Dbg(size, file, lineno);
|
||||||
|
|
||||||
if (memblock != NULL)
|
size_t oldsize = _msize(memblock);
|
||||||
{
|
|
||||||
GC::AllocBytes -= _msize(memblock);
|
|
||||||
}
|
|
||||||
void *block = _realloc_dbg(((size_t*) memblock)-1, size+sizeof(size_t), _NORMAL_BLOCK, file, lineno);
|
void *block = _realloc_dbg(((size_t*) memblock)-1, size+sizeof(size_t), _NORMAL_BLOCK, file, lineno);
|
||||||
|
|
||||||
if (block == NULL)
|
if (block == nullptr)
|
||||||
{
|
{
|
||||||
I_FatalError("Could not realloc %zu bytes in %s, line %d", size, file, lineno);
|
I_FatalError("Could not realloc %zu bytes in %s, line %d", size, file, lineno);
|
||||||
}
|
}
|
||||||
|
@ -186,29 +174,22 @@ void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno)
|
||||||
*sizeStore = size;
|
*sizeStore = size;
|
||||||
block = sizeStore+1;
|
block = sizeStore+1;
|
||||||
|
|
||||||
GC::AllocBytes += _msize(block);
|
GC::ReportRealloc(oldsize, _msize(block));
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void M_Free (void *block)
|
||||||
|
{
|
||||||
|
if (block != nullptr)
|
||||||
|
{
|
||||||
|
GC::ReportDealloc(_msize(block));
|
||||||
#if !defined(__solaris__) && !defined(__OpenBSD__) && !defined(__DragonFly__)
|
#if !defined(__solaris__) && !defined(__OpenBSD__) && !defined(__DragonFly__)
|
||||||
void M_Free (void *block)
|
|
||||||
{
|
|
||||||
if (block != NULL)
|
|
||||||
{
|
|
||||||
GC::AllocBytes -= _msize(block);
|
|
||||||
free(block);
|
free(block);
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
void M_Free (void *block)
|
|
||||||
{
|
|
||||||
if(block != NULL)
|
|
||||||
{
|
|
||||||
GC::AllocBytes -= _msize(block);
|
|
||||||
free(((size_t*) block)-1);
|
free(((size_t*) block)-1);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
|
@ -371,7 +371,7 @@ class ScreenJobRunner : Object UI
|
||||||
|
|
||||||
bool CanWipe()
|
bool CanWipe()
|
||||||
{
|
{
|
||||||
if (index < jobs.Size()) return !jobs[index].nowipe;
|
if (index < jobs.Size()) return !jobs[max(0, index)].nowipe;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue