From 983c0c56b1777317d4f69bbbbf58fc0055221ff3 Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <coelckers@users.noreply.github.com>
Date: Sun, 18 Nov 2018 01:06:04 +0100
Subject: [PATCH] - changed OP_PARAM handling so that all registers remain
 allocated until the call instruction and reordered instruction emission so
 that the param instructions all directly precede the call instruction.

---
 src/d_dehacked.cpp                  | 130 ++++++++-----------
 src/scripting/backend/codegen.cpp   | 190 ++++++++++++----------------
 src/scripting/backend/vmbuilder.cpp | 131 +++++++++++++++----
 src/scripting/backend/vmbuilder.h   |  31 ++++-
 4 files changed, 273 insertions(+), 209 deletions(-)

diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp
index de7dcba7ae..d3bd4334f5 100644
--- a/src/d_dehacked.cpp
+++ b/src/d_dehacked.cpp
@@ -634,126 +634,102 @@ static int GetLine (void)
 
 
 // misc1 = vrange (arg +3), misc2 = hrange (arg+4)
-static int CreateMushroomFunc(VMFunctionBuilder &buildit, int value1, int value2)
+static void CreateMushroomFunc(EmitterArray &emitters, int value1, int value2)
 { // A_Mushroom
-	int typereg = buildit.GetConstantAddress(PClass::FindClass("FatShot"));
-	buildit.Emit(OP_PARAM, REGT_POINTER | REGT_KONST, typereg);	// itemtype
-	buildit.Emit(OP_PARAMI, 0);					// numspawns
-	buildit.Emit(OP_PARAMI, 1);					// flag
-	// vrange
-	buildit.Emit(OP_PARAM, REGT_FLOAT | REGT_KONST, buildit.GetConstantFloat(value1? DEHToDouble(value1) : 4.0));
-	// hrange
-	buildit.Emit(OP_PARAM, REGT_FLOAT | REGT_KONST, buildit.GetConstantFloat(value2? DEHToDouble(value2) : 0.5));
-	return 5;
+	emitters.AddParameterPointerConst(PClass::FindClass("FatShot"));	// itemtype
+	emitters.AddParameterIntConst(0);									// numspawns
+	emitters.AddParameterIntConst(1);									// flag MSF_Classic
+	emitters.AddParameterFloatConst(value1? DEHToDouble(value1) : 4.0);	// vrange
+	emitters.AddParameterFloatConst(value2? DEHToDouble(value2) : 0.5);	// hrange
 }
 
 // misc1 = type (arg +0), misc2 = Z-pos (arg +2)
-static int CreateSpawnFunc(VMFunctionBuilder &buildit, int value1, int value2)
+static void CreateSpawnFunc(EmitterArray &emitters, int value1, int value2)
 { // A_SpawnItem
-	if (InfoNames[value1-1] == NULL)
+	if (InfoNames[value1-1] == nullptr)
 	{
 		I_Error("No class found for dehackednum %d!\n", value1+1);
-		return 0;
 	}
-	int typereg = buildit.GetConstantAddress(InfoNames[value1-1]);
-	int heightreg = buildit.GetConstantFloat(value2);
-	int distreg = buildit.GetConstantFloat(0);
-
-	buildit.Emit(OP_PARAM, REGT_POINTER | REGT_KONST, typereg);	// itemtype
-	buildit.Emit(OP_PARAM, REGT_FLOAT | REGT_KONST, distreg);	// distance
-	buildit.Emit(OP_PARAM, REGT_FLOAT | REGT_KONST, heightreg);	// height
-	buildit.Emit(OP_PARAMI, 0);									// useammo
-	buildit.Emit(OP_PARAMI, 0);									// transfer_translation
-	return 5;
+	emitters.AddParameterPointerConst(InfoNames[value1-1]);	// itemtype
+	emitters.AddParameterFloatConst(value2);				// distance
+	emitters.AddParameterFloatConst(0);						// height
+	emitters.AddParameterIntConst(0);						// useammo
+	emitters.AddParameterIntConst(0);						// transfer_translation
 }
 
 
 // misc1 = angle (in degrees) (arg +0 but factor in current actor angle too)
-static int CreateTurnFunc(VMFunctionBuilder &buildit, int value1, int value2)
+static void CreateTurnFunc(EmitterArray &emitters, int value1, int value2)
 { // A_Turn
-	buildit.Emit(OP_PARAM, REGT_FLOAT | REGT_KONST, buildit.GetConstantFloat(value1));		// angle
-	return 1;
+	emitters.AddParameterFloatConst(value1);				// angle
 }
 
 // misc1 = angle (in degrees) (arg +0)
-static int CreateFaceFunc(VMFunctionBuilder &buildit, int value1, int value2)
+static void CreateFaceFunc(EmitterArray &emitters, int value1, int value2)
 { // A_FaceTarget
-	buildit.Emit(OP_PARAM, REGT_FLOAT | REGT_KONST, buildit.GetConstantFloat(value1));		// angle
-	buildit.Emit(OP_PARAMI, 0);																// flags
-	buildit.Emit(OP_PARAMI, AAPTR_DEFAULT);													// ptr
-	return 3;
+	emitters.AddParameterFloatConst(value1);				// angle
+	emitters.AddParameterIntConst(0);						// flags
+	emitters.AddParameterIntConst(AAPTR_DEFAULT);			// ptr
 }
 
 // misc1 = damage, misc 2 = sound
-static int CreateScratchFunc(VMFunctionBuilder &buildit, int value1, int value2)
+static void CreateScratchFunc(EmitterArray &emitters, int value1, int value2)
 { // A_CustomMeleeAttack
-	buildit.EmitParamInt(value1);							// damage
-	buildit.EmitParamInt(value2? SoundMap[value2 - 1] : 0);	// hit sound
-	buildit.Emit(OP_PARAMI, 0);								// miss sound
-	buildit.Emit(OP_PARAMI, NAME_None);						// damage type
-	buildit.Emit(OP_PARAMI, true);							// bleed
-	return 5;
+	emitters.AddParameterIntConst(value1);								// damage
+	emitters.AddParameterIntConst(value2 ? SoundMap[value2 - 1] : 0);	// hit sound
+	emitters.AddParameterIntConst(0);									// miss sound
+	emitters.AddParameterIntConst(NAME_None);							// damage type
+	emitters.AddParameterIntConst(true);								// bleed
 }
 
 // misc1 = sound, misc2 = attenuation none (true) or normal (false)
-static int CreatePlaySoundFunc(VMFunctionBuilder &buildit, int value1, int value2)
+static void CreatePlaySoundFunc(EmitterArray &emitters, int value1, int value2)
 { // A_PlaySound
-	int float1 = buildit.GetConstantFloat(1);
-	int attenreg = buildit.GetConstantFloat(value2 ? ATTN_NONE : ATTN_NORM);
-
-	buildit.EmitParamInt(SoundMap[value1-1]);						// soundid
-	buildit.Emit(OP_PARAMI, CHAN_BODY);								// channel
-	buildit.Emit(OP_PARAM, REGT_FLOAT | REGT_KONST, float1);		// volume
-	buildit.Emit(OP_PARAMI, false);									// looping
-	buildit.Emit(OP_PARAM, REGT_FLOAT | REGT_KONST, attenreg);	// attenuation
-	buildit.Emit(OP_PARAMI, false);									// local
-	return 6;
+	emitters.AddParameterIntConst(value1 ? SoundMap[value1 - 1] : 0);	// soundid
+	emitters.AddParameterIntConst(CHAN_BODY);							// channel
+	emitters.AddParameterFloatConst(1);									// volume
+	emitters.AddParameterIntConst(false);								// looping
+	emitters.AddParameterFloatConst(value2 ? ATTN_NONE : ATTN_NORM);	// attenuation
+	emitters.AddParameterIntConst(false);								// local
 }
 
 // misc1 = state, misc2 = probability
-static int CreateRandomJumpFunc(VMFunctionBuilder &buildit, int value1, int value2)
+static void CreateRandomJumpFunc(EmitterArray &emitters, int value1, int value2)
 { // A_Jump
-	int statereg = buildit.GetConstantAddress(FindState(value1));
-
-	buildit.EmitParamInt(value2);									// maxchance
-	buildit.Emit(OP_PARAM, REGT_POINTER | REGT_KONST, statereg);	// jumpto
-	return 2;
+	emitters.AddParameterIntConst(value2);					// maxchance
+	emitters.AddParameterPointerConst(FindState(value1));	// jumpto
 }
 
 // misc1 = Boom linedef type, misc2 = sector tag
-static int CreateLineEffectFunc(VMFunctionBuilder &buildit, int value1, int value2)
+static void CreateLineEffectFunc(EmitterArray &emitters, int value1, int value2)
 { // A_LineEffect
 	// This is the second MBF codepointer that couldn't be translated easily.
 	// Calling P_TranslateLineDef() here was a simple matter, as was adding an
 	// extra parameter to A_CallSpecial so as to replicate the LINEDONE stuff,
 	// but unfortunately DEHACKED lumps are processed before the map translation
 	// arrays are initialized so this didn't work.
-	buildit.EmitParamInt(value1);					// special
-	buildit.EmitParamInt(value2);					// tag
-	return 2;
+	emitters.AddParameterIntConst(value1);		// special
+	emitters.AddParameterIntConst(value2);		// tag
 }
 
 // No misc, but it's basically A_Explode with an added effect
-static int CreateNailBombFunc(VMFunctionBuilder &buildit, int value1, int value2)
+static void CreateNailBombFunc(EmitterArray &emitters, int value1, int value2)
 { // A_Explode
 	// This one does not actually have MBF-style parameters. But since
 	// we're aliasing it to an extension of A_Explode...
-	int typereg = buildit.GetConstantAddress(PClass::FindClass(NAME_BulletPuff));
-	buildit.Emit(OP_PARAMI, -1);		// damage
-	buildit.Emit(OP_PARAMI, -1);		// distance
-	buildit.Emit(OP_PARAMI, 1);			// flags (1=XF_HURTSOURCE)
-	buildit.Emit(OP_PARAMI, 0);			// alert
-	buildit.Emit(OP_PARAMI, 0);			// fulldamagedistance
-	buildit.Emit(OP_PARAMI, 30);		// nails
-	buildit.Emit(OP_PARAMI, 10);		// naildamage
-	buildit.Emit(OP_PARAM, REGT_POINTER | REGT_KONST, typereg);	// itemtype
-	buildit.Emit(OP_PARAMI, NAME_None);						// damage type
-
-	return 9;
+	emitters.AddParameterIntConst(-1);		// damage
+	emitters.AddParameterIntConst(-1);		// distance
+	emitters.AddParameterIntConst(1);		// flags (1=XF_HURTSOURCE)
+	emitters.AddParameterIntConst(0);		// alert
+	emitters.AddParameterIntConst(0);		// fulldamagedistance
+	emitters.AddParameterIntConst(30);		// nails
+	emitters.AddParameterIntConst(10);		// naildamage
+	emitters.AddParameterPointerConst(PClass::FindClass(NAME_BulletPuff));	// itemtype
+	emitters.AddParameterIntConst(NAME_None);	// damage type
 }
 
 // This array must be in sync with the Aliases array in DEHSUPP.
-static int (*MBFCodePointerFactories[])(VMFunctionBuilder&, int, int) =
+static void (*MBFCodePointerFactories[])(EmitterArray&, int, int) =
 {
 	// Die and Detonate are not in this list because these codepointers have
 	// no dehacked arguments and therefore do not need special handling.
@@ -800,13 +776,15 @@ void SetDehParams(FState *state, int codepointer)
 		// self, stateowner, state (all are pointers)
 		buildit.Registers[REGT_POINTER].Get(numargs);
 		// Emit code to pass the standard action function parameters.
+		EmitterArray emitters;
 		for (int i = 0; i < numargs; i++)
 		{
-			buildit.Emit(OP_PARAM, REGT_POINTER, i);
+			emitters.AddParameterPointer(i, false);
 		}
 		// Emit code for action parameters.
-		int argcount = MBFCodePointerFactories[codepointer](buildit, value1, value2);
-		buildit.Emit(OP_TAIL_K, buildit.GetConstantAddress(sym->Variants[0].Implementation), numargs + argcount, 0);
+		MBFCodePointerFactories[codepointer](emitters, value1, value2);
+		int count = emitters.EmitParameters(&buildit);
+		buildit.Emit(OP_TAIL_K, buildit.GetConstantAddress(sym->Variants[0].Implementation), count, 0);
 		// Attach it to the state.
 		VMScriptFunction *sfunc = new VMScriptFunction;
 		buildit.MakeFunction(sfunc);
diff --git a/src/scripting/backend/codegen.cpp b/src/scripting/backend/codegen.cpp
index 07a3f1758b..26e2ccd99b 100644
--- a/src/scripting/backend/codegen.cpp
+++ b/src/scripting/backend/codegen.cpp
@@ -485,7 +485,7 @@ PPrototype *FxExpression::ReturnProto()
 //
 //==========================================================================
 
-static int EncodeRegType(ExpEmit reg)
+int EncodeRegType(ExpEmit reg)
 {
 	int regtype = reg.RegType;
 	if (reg.Fixed && reg.Target)
@@ -508,36 +508,6 @@ static int EncodeRegType(ExpEmit reg)
 	return regtype;
 }
 
-//==========================================================================
-//
-//
-//
-//==========================================================================
-
-static int EmitParameter(VMFunctionBuilder *build, FxExpression *operand, const FScriptPosition &pos, TArray<ExpEmit> *tempstrings = nullptr)
-{
-	ExpEmit where = operand->Emit(build);
-
-	if (where.RegType == REGT_NIL)
-	{
-		pos.Message(MSG_ERROR, "Attempted to pass a non-value");
-		build->Emit(OP_PARAM, where.RegType, where.RegNum);
-		return 1;
-	}
-	else
-	{
-		build->Emit(OP_PARAM, EncodeRegType(where), where.RegNum);
-		if (tempstrings != nullptr && where.RegType == REGT_STRING && !where.Fixed && !where.Konst)
-		{
-			tempstrings->Push(where);	// keep temp strings until after the function call.
-		}
-		else
-		{
-			where.Free(build);
-		}
-		return where.RegCount;
-	}
-}
 
 //==========================================================================
 //
@@ -5539,10 +5509,13 @@ ExpEmit FxRandom::Emit(VMFunctionBuilder *build)
 	if (build->FramePointer.Fixed) EmitTail = false;	// do not tail call if the stack is in use
 	int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K);
 
-	build->Emit(OP_PARAM, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng));
-	EmitParameter(build, min, ScriptPosition);
-	EmitParameter(build, max, ScriptPosition);
-	build->Emit(opcode, build->GetConstantAddress(callfunc), 3, 1);
+	EmitterArray emitters;
+
+	emitters.AddParameterPointerConst(rng);
+	emitters.AddParameter(build, min);
+	emitters.AddParameter(build, max);
+	int count = emitters.EmitParameters(build);
+	build->Emit(opcode, build->GetConstantAddress(callfunc), count, 1);
 
 	if (EmitTail)
 	{
@@ -5653,10 +5626,12 @@ ExpEmit FxRandomPick::Emit(VMFunctionBuilder *build)
 	assert(((PSymbolVMFunction *)sym)->Function != nullptr);
 	callfunc = ((PSymbolVMFunction *)sym)->Function;
 
-	build->Emit(OP_PARAM, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng));
-	build->EmitParamInt(0);
-	build->EmitParamInt(choices.Size() - 1);
-	build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), 3, 1);
+	EmitterArray emitters;
+	emitters.AddParameterPointerConst(rng);
+	emitters.AddParameterIntConst(0);
+	emitters.AddParameterIntConst(choices.Size() - 1);
+	int count = emitters.EmitParameters(build);
+	build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 1);
 
 	ExpEmit resultreg(build, REGT_INT);
 	build->Emit(OP_RESULT, 0, REGT_INT, resultreg.RegNum);
@@ -5775,10 +5750,12 @@ ExpEmit FxFRandom::Emit(VMFunctionBuilder *build)
 	if (build->FramePointer.Fixed) EmitTail = false;	// do not tail call if the stack is in use
 	int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K);
 
-	build->Emit(OP_PARAM, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng));
-	EmitParameter(build, min, ScriptPosition);
-	EmitParameter(build, max, ScriptPosition);
-	build->Emit(opcode, build->GetConstantAddress(callfunc), 3, 1);
+	EmitterArray emitters;
+	emitters.AddParameterPointerConst(rng);
+	emitters.AddParameter(build, min);
+	emitters.AddParameter(build, max);
+	int count = emitters.EmitParameters(build);
+	build->Emit(opcode, build->GetConstantAddress(callfunc), count, 1);
 
 	if (EmitTail)
 	{
@@ -5877,9 +5854,12 @@ ExpEmit FxRandom2::Emit(VMFunctionBuilder *build)
 	if (build->FramePointer.Fixed) EmitTail = false;	// do not tail call if the stack is in use
 	int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K);
 
-	build->Emit(OP_PARAM, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng));
-	EmitParameter(build, mask, ScriptPosition);
-	build->Emit(opcode, build->GetConstantAddress(callfunc), 2, 1);
+	EmitterArray emitters;
+
+	emitters.AddParameterPointerConst(rng);
+	emitters.AddParameter(build, mask);
+	int count = emitters.EmitParameters(build);
+	build->Emit(opcode, build->GetConstantAddress(callfunc), count, 1);
 
 	if (EmitTail)
 	{
@@ -5960,9 +5940,12 @@ ExpEmit FxRandomSeed::Emit(VMFunctionBuilder *build)
 	if (build->FramePointer.Fixed) EmitTail = false;	// do not tail call if the stack is in use
 	int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K);
 
-	build->Emit(OP_PARAM, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng));
-	EmitParameter(build, seed, ScriptPosition);
-	build->Emit(opcode, build->GetConstantAddress(callfunc), 2, 0);
+	EmitterArray emitters;
+
+	emitters.AddParameterPointerConst(rng);
+	emitters.AddParameter(build, seed);
+	int count = emitters.EmitParameters(build);
+	build->Emit(opcode, build->GetConstantAddress(callfunc), count, 0);
 
 	ExpEmit call;
 	if (EmitTail) call.Final = true;
@@ -8642,10 +8625,13 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build)
 {
 	unsigned i = 0;
 
-	build->Emit(OP_PARAMI, abs(Special));			// pass special number
+	EmitterArray emitters;
+
+	emitters.AddParameterIntConst(abs(Special));			// pass special number
 
 	ExpEmit selfemit(Self->Emit(build));
-	build->Emit(OP_PARAM, selfemit.Konst ? REGT_POINTER | REGT_KONST : REGT_POINTER, selfemit.RegNum);			// pass special number
+	emitters.AddParameterPointer(selfemit.RegNum, selfemit.Konst);
+
 	for (; i < ArgList.Size(); ++i)
 	{
 		FxExpression *argex = ArgList[i];
@@ -8653,20 +8639,18 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build)
 		{
 			assert(argex->ValueType == TypeName);
 			assert(argex->isConstant());
-			build->EmitParamInt(-static_cast<FxConstant *>(argex)->GetValue().GetName());
+			emitters.AddParameterIntConst(-static_cast<FxConstant *>(argex)->GetValue().GetName());
 		}
 		else
 		{
 			assert(argex->ValueType->GetRegType() == REGT_INT);
 			if (argex->isConstant())
 			{
-				build->EmitParamInt(static_cast<FxConstant *>(argex)->GetValue().GetInt());
+				emitters.AddParameterIntConst(static_cast<FxConstant *>(argex)->GetValue().GetInt());
 			}
 			else
 			{
-				ExpEmit arg(argex->Emit(build));
-				build->Emit(OP_PARAM, arg.RegType, arg.RegNum);
-				arg.Free(build);
+				emitters.AddParameter(build, argex);
 			}
 		}
 	}
@@ -8681,16 +8665,18 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build)
 	ArgList.ShrinkToFit();
 
 	if (build->FramePointer.Fixed) EmitTail = false;	// do not tail call if the stack is in use
+	int count = emitters.EmitParameters(build);
+	selfemit.Free(build);
 	if (EmitTail)
 	{
-		build->Emit(OP_TAIL_K, build->GetConstantAddress(callfunc), 2 + i, 0);
+		build->Emit(OP_TAIL_K, build->GetConstantAddress(callfunc), count, 0);
 		ExpEmit call;
 		call.Final = true;
 		return call;
 	}
 
 	ExpEmit dest(build, REGT_INT);
-	build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), 2 + i, 1);
+	build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 1);
 	build->Emit(OP_RESULT, 0, REGT_INT, dest.RegNum);
 	return dest;
 }
@@ -9079,8 +9065,6 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx)
 
 ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
 {
-	TArray<ExpEmit> tempstrings;
-
 	assert(build->Registers[REGT_POINTER].GetMostUsed() >= build->NumImplicits);
 	int count = 0;
 
@@ -9093,7 +9077,6 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
 		{
 			ArgList.DeleteAndClear();
 			ArgList.ShrinkToFit();
-			for (auto & exp : tempstrings) exp.Free(build);
 			return reg;
 		}
 	}
@@ -9102,6 +9085,7 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
 	bool staticcall = ((vmfunc->VarFlags & VARF_Final) || vmfunc->VirtualIndex == ~0u || NoVirtual);
 
 	count = 0;
+	EmitterArray emitters;
 	// Emit code to pass implied parameters
 	ExpEmit selfemit;
 	if (Function->Variants[0].Flags & VARF_Method)
@@ -9128,64 +9112,51 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
 			}
 		}
 
-		if ((selfemit.Fixed && selfemit.Target) || selfemit.RegType == REGT_STRING)
-		{
-			// Address of a local variable.
-			build->Emit(OP_PARAM, selfemit.RegType | REGT_ADDROF, selfemit.RegNum);
-		}
-		else
-		{
-			build->Emit(OP_PARAM, selfemit.RegType, selfemit.RegNum);
-		}
-		count += 1;
+		emitters.AddParameter(selfemit, (selfemit.Fixed && selfemit.Target) || selfemit.RegType == REGT_STRING);
 		if (Function->Variants[0].Flags & VARF_Action)
 		{
 			static_assert(NAP == 3, "This code needs to be updated if NAP changes");
 			if (build->NumImplicits == NAP && selfemit.RegNum == 0)	// only pass this function's stateowner and stateinfo if the subfunction is run in self's context.
 			{
-				build->Emit(OP_PARAM, REGT_POINTER, 1);
-				build->Emit(OP_PARAM, REGT_POINTER, 2);
+				emitters.AddParameterPointer(1, false);
+				emitters.AddParameterPointer(2, false);
 			}
 			else
 			{
 				// pass self as stateowner, otherwise all attempts of the subfunction to retrieve a state from a name would fail.
-				build->Emit(OP_PARAM, selfemit.RegType, selfemit.RegNum);
-				build->Emit(OP_PARAM, REGT_POINTER | REGT_KONST, build->GetConstantAddress(nullptr));
+				emitters.AddParameter(selfemit, (selfemit.Fixed && selfemit.Target) || selfemit.RegType == REGT_STRING);
+				emitters.AddParameterPointerConst(nullptr);
 			}
-			count += 2;
 		}
-		if (staticcall) selfemit.Free(build);
 	}
 	else staticcall = true;
 	// Emit code to pass explicit parameters
 	for (unsigned i = 0; i < ArgList.Size(); ++i)
 	{
-		count += EmitParameter(build, ArgList[i], ScriptPosition, &tempstrings);
+		emitters.AddParameter(build, ArgList[i]);
 	}
 	// Complete the parameter list from the defaults.
 	auto &defaults = Function->Variants[0].Implementation->DefaultArgs;
-	for (unsigned i = count; i < defaults.Size(); i++)
+	for (unsigned i = emitters.Count(); i < defaults.Size(); i++)
 	{
-		FxConstant *constant;
 		switch (defaults[i].Type)
 		{
 		default:
 		case REGT_INT:
-			constant = new FxConstant(defaults[i].i, ScriptPosition);
+			emitters.AddParameterIntConst(defaults[i].i);
 			break;
 		case REGT_FLOAT:
-			constant = new FxConstant(defaults[i].f, ScriptPosition);
+			emitters.AddParameterFloatConst(defaults[i].f);
 			break;
 		case REGT_POINTER:
-			constant = new FxConstant(defaults[i].a, ScriptPosition);
+			emitters.AddParameterPointerConst(defaults[i].a);
 			break;
 		case REGT_STRING:
-			constant = new FxConstant(defaults[i].s(), ScriptPosition);
+			emitters.AddParameterStringConst(defaults[i].s());
 			break;
 		}
-		count += EmitParameter(build, constant, ScriptPosition, &tempstrings);
-		delete constant;
 	}
+	count = emitters.EmitParameters(build);
 
 	ArgList.DeleteAndClear();
 	ArgList.ShrinkToFit();
@@ -9200,7 +9171,6 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
 			build->Emit(OP_TAIL_K, funcaddr, count, 0);
 			ExpEmit call;
 			call.Final = true;
-			for (auto & exp : tempstrings) exp.Free(build);
 			return call;
 		}
 		else if (vmfunc->Proto->ReturnTypes.Size() > 0)
@@ -9211,13 +9181,11 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
 		else
 		{ // Call, expecting no results
 			build->Emit(OP_CALL_K, funcaddr, count, 0);
-			for (auto & exp : tempstrings) exp.Free(build);
 			return ExpEmit();
 		}
 	}
 	else
 	{
-		selfemit.Free(build);
 		ExpEmit funcreg(build, REGT_POINTER);
 
 		build->Emit(OP_VTBL, funcreg.RegNum, selfemit.RegNum, vmfunc->VirtualIndex);
@@ -9226,7 +9194,6 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
 			build->Emit(OP_TAIL, funcreg.RegNum, count, 0);
 			ExpEmit call;
 			call.Final = true;
-			for (auto & exp : tempstrings) exp.Free(build);
 			return call;
 		}
 		else if (vmfunc->Proto->ReturnTypes.Size() > 0)
@@ -9237,7 +9204,6 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
 		else
 		{ // Call, expecting no results
 			build->Emit(OP_CALL, funcreg.RegNum, count, 0);
-			for (auto & exp : tempstrings) exp.Free(build);
 			return ExpEmit();
 		}
 	}
@@ -9247,7 +9213,6 @@ handlereturns:
 		// Regular call, will not write to ReturnRegs
 		ExpEmit reg(build, vmfunc->Proto->ReturnTypes[0]->GetRegType(), vmfunc->Proto->ReturnTypes[0]->GetRegCount());
 		build->Emit(OP_RESULT, 0, EncodeRegType(reg), reg.RegNum);
-		for (auto & exp : tempstrings) exp.Free(build);
 		return reg;
 	}
 	else
@@ -9261,7 +9226,6 @@ handlereturns:
 			ReturnRegs.Push(reg);
 		}
 	}
-	for (auto & exp : tempstrings) exp.Free(build);
 	return ExpEmit();
 }
 
@@ -10773,9 +10737,10 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build)
 		assert(pstr->mDestructor != nullptr);
 		ExpEmit reg(build, REGT_POINTER);
 		build->Emit(OP_ADDA_RK, reg.RegNum, build->FramePointer.RegNum, build->GetConstantInt(build->ConstructedStructs[i]->StackOffset));
-		build->Emit(OP_PARAM, reg.RegType, reg.RegNum);
-		build->Emit(OP_CALL_K, build->GetConstantAddress(pstr->mDestructor), 1, 0);
-		reg.Free(build);
+		EmitterArray emitters;
+		emitters.AddParameter(reg, false);
+		int count = emitters.EmitParameters(build);
+		build->Emit(OP_CALL_K, build->GetConstantAddress(pstr->mDestructor), count, 0);
 	}
 
 	// If we return nothing, use a regular RET opcode.
@@ -10970,8 +10935,9 @@ ExpEmit FxClassTypeCast::Emit(VMFunctionBuilder *build)
 	ExpEmit clsname = basex->Emit(build);
 	assert(!clsname.Konst);
 	ExpEmit dest(build, REGT_POINTER);
-	build->Emit(OP_PARAM, clsname.RegType, clsname.RegNum);
-	build->Emit(OP_PARAM, REGT_POINTER | REGT_KONST, build->GetConstantAddress(const_cast<PClass *>(desttype)));
+	EmitterArray emitters;
+	emitters.AddParameter(clsname, false);
+	emitters.AddParameterPointerConst(const_cast<PClass *>(desttype));
 
 	// Call the BuiltinNameToClass function to convert from 'name' to class.
 	VMFunction *callfunc;
@@ -10981,9 +10947,9 @@ ExpEmit FxClassTypeCast::Emit(VMFunctionBuilder *build)
 	assert(((PSymbolVMFunction *)sym)->Function != nullptr);
 	callfunc = ((PSymbolVMFunction *)sym)->Function;
 
-	build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), 2, 1);
+	int count = emitters.EmitParameters(build);
+	build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 1);
 	build->Emit(OP_RESULT, 0, REGT_POINTER, dest.RegNum);
-	clsname.Free(build);
 	return dest;
 }
 
@@ -11079,8 +11045,10 @@ ExpEmit FxClassPtrCast::Emit(VMFunctionBuilder *build)
 {
 	ExpEmit clsname = basex->Emit(build);
 
-	build->Emit(OP_PARAM, clsname.RegType, clsname.RegNum);
-	build->Emit(OP_PARAM, REGT_POINTER | REGT_KONST, build->GetConstantAddress(desttype));
+	EmitterArray emitters;
+
+	emitters.AddParameter(clsname, false);
+	emitters.AddParameterPointerConst(desttype);
 
 	VMFunction *callfunc;
 	PSymbol *sym = FindBuiltinFunction(NAME_BuiltinClassCast, BuiltinClassCast);
@@ -11088,9 +11056,9 @@ ExpEmit FxClassPtrCast::Emit(VMFunctionBuilder *build)
 	assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction)));
 	assert(((PSymbolVMFunction *)sym)->Function != nullptr);
 	callfunc = ((PSymbolVMFunction *)sym)->Function;
-	clsname.Free(build);
 	ExpEmit dest(build, REGT_POINTER);
-	build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), 2, 1);
+	int count = emitters.EmitParameters(build);
+	build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc), count, 1);
 	build->Emit(OP_RESULT, 0, REGT_POINTER, dest.RegNum);
 	return dest;
 }
@@ -11465,9 +11433,10 @@ ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build)
 			{
 				ExpEmit reg(build, REGT_POINTER);
 				build->Emit(OP_ADDA_RK, reg.RegNum, build->FramePointer.RegNum, build->GetConstantInt(StackOffset));
-				build->Emit(OP_PARAM, reg.RegType, reg.RegNum);
-				build->Emit(OP_CALL_K, build->GetConstantAddress(pstr->mConstructor), 1, 0);
-				reg.Free(build);
+				EmitterArray emitters;
+				emitters.AddParameter(reg, false);
+				int count = emitters.EmitParameters(build);
+				build->Emit(OP_CALL_K, build->GetConstantAddress(pstr->mConstructor), count, 0);
 			}
 			if (pstr->mDestructor != nullptr) build->ConstructedStructs.Push(this);
 		}
@@ -11491,9 +11460,10 @@ void FxLocalVariableDeclaration::Release(VMFunctionBuilder *build)
 			{
 				ExpEmit reg(build, REGT_POINTER);
 				build->Emit(OP_ADDA_RK, reg.RegNum, build->FramePointer.RegNum, build->GetConstantInt(StackOffset));
-				build->Emit(OP_PARAM, reg.RegType, reg.RegNum);
-				build->Emit(OP_CALL_K, build->GetConstantAddress(pstr->mDestructor), 1, 0);
-				reg.Free(build);
+				EmitterArray emitters;
+				emitters.AddParameter(reg, false);
+				int count = emitters.EmitParameters(build);
+				build->Emit(OP_CALL_K, build->GetConstantAddress(pstr->mDestructor), count, 0);
 			}
 			build->ConstructedStructs.Delete(build->ConstructedStructs.Find(this));
 		}
diff --git a/src/scripting/backend/vmbuilder.cpp b/src/scripting/backend/vmbuilder.cpp
index c9df0c5e6c..6c16e1a3a9 100644
--- a/src/scripting/backend/vmbuilder.cpp
+++ b/src/scripting/backend/vmbuilder.cpp
@@ -654,28 +654,6 @@ size_t VMFunctionBuilder::Emit(int opcode, int opabc)
 	return Code.Push(op);
 }
 
-//==========================================================================
-//
-// VMFunctionBuilder :: EmitParamInt
-//
-// Passes a constant integer parameter, using either PARAMI and an immediate
-// value or PARAM and a constant register, as appropriate.
-//
-//==========================================================================
-
-size_t VMFunctionBuilder::EmitParamInt(int value)
-{
-	// Immediates for PARAMI must fit in 24 bits.
-	if (((value << 8) >> 8) == value)
-	{
-		return Emit(OP_PARAMI, value);
-	}
-	else
-	{
-		return Emit(OP_PARAM, REGT_INT | REGT_KONST, GetConstantInt(value));
-	}
-}
-
 //==========================================================================
 //
 // VMFunctionBuilder :: EmitLoadInt
@@ -951,3 +929,112 @@ void FFunctionBuildList::DumpJit()
 
 	fclose(dump);
 }
+
+
+void EmitterArray::AddParameter(VMFunctionBuilder *build, FxExpression *operand)
+{
+	ExpEmit where = operand->Emit(build);
+
+	if (where.RegType == REGT_NIL)
+	{
+		operand->ScriptPosition.Message(MSG_ERROR, "Attempted to pass a non-value");
+	}
+	numparams += where.RegCount;
+
+	emitters.push_back([=](VMFunctionBuilder *build) -> int
+	{
+		auto op = where;
+		if (op.RegType == REGT_NIL)
+		{
+			build->Emit(OP_PARAM, op.RegType, op.RegNum);
+			return 1;
+		}
+		else
+		{
+			build->Emit(OP_PARAM, EncodeRegType(op), op.RegNum);
+			op.Free(build);
+			return op.RegCount;
+		}
+	});
+}
+
+void EmitterArray::AddParameter(ExpEmit &emit, bool reference)
+{
+	numparams += emit.RegCount;
+	emitters.push_back([=](VMFunctionBuilder *build) ->int
+	{
+		build->Emit(OP_PARAM, emit.RegType + (reference * REGT_ADDROF), emit.RegNum);
+		auto op = emit;
+		op.Free(build);
+		return emit.RegCount;
+	});
+}
+
+void EmitterArray::AddParameterPointerConst(void *konst)
+{
+	numparams++;
+	emitters.push_back([=](VMFunctionBuilder *build) ->int
+	{
+		build->Emit(OP_PARAM, REGT_POINTER | REGT_KONST, build->GetConstantAddress(konst));
+		return 1;
+	});
+}
+
+void EmitterArray::AddParameterPointer(int index, bool konst)
+{
+	numparams++;
+	emitters.push_back([=](VMFunctionBuilder *build) ->int
+	{
+		build->Emit(OP_PARAM, konst ? REGT_POINTER | REGT_KONST : REGT_POINTER, index);
+		return 1;
+	});
+}
+
+void EmitterArray::AddParameterFloatConst(double konst)
+{
+	numparams++;
+	emitters.push_back([=](VMFunctionBuilder *build) ->int
+	{
+		build->Emit(OP_PARAM, REGT_FLOAT | REGT_KONST, build->GetConstantFloat(konst));
+		return 1;
+	});
+}
+
+void EmitterArray::AddParameterIntConst(int konst)
+{
+	numparams++;
+	emitters.push_back([=](VMFunctionBuilder *build) ->int
+	{
+		// Immediates for PARAMI must fit in 24 bits.
+		if (((konst << 8) >> 8) == konst)
+		{
+			build->Emit(OP_PARAMI, konst);
+		}
+		else
+		{
+			build->Emit(OP_PARAM, REGT_INT | REGT_KONST, build->GetConstantInt(konst));
+		}
+		return 1;
+	});
+}
+
+void EmitterArray::AddParameterStringConst(const FString &konst)
+{
+	numparams++;
+	emitters.push_back([=](VMFunctionBuilder *build) ->int
+	{
+		build->Emit(OP_PARAM, REGT_STRING | REGT_KONST, build->GetConstantString(konst));
+		return 1;
+	});
+}
+
+int EmitterArray::EmitParameters(VMFunctionBuilder *build)
+{
+	int paramcount = 0;
+	for (auto &func : emitters)
+	{
+		paramcount += func(build);
+	}
+	assert(paramcount == numparams);
+	return paramcount;
+}
diff --git a/src/scripting/backend/vmbuilder.h b/src/scripting/backend/vmbuilder.h
index cbac7a2393..38e05de9bb 100644
--- a/src/scripting/backend/vmbuilder.h
+++ b/src/scripting/backend/vmbuilder.h
@@ -68,7 +68,6 @@ public:
 	size_t Emit(int opcode, int opa, int opb, int opc);
 	size_t Emit(int opcode, int opa, VM_SHALF opbc);
 	size_t Emit(int opcode, int opabc);
-	size_t EmitParamInt(int value);
 	size_t EmitLoadInt(int regnum, int value);
 	size_t EmitRetInt(int retnum, bool final, int value);
 
@@ -154,4 +153,34 @@ public:
 };
 
 extern FFunctionBuildList FunctionBuildList;
+
+
+//==========================================================================
+//
+// Function call parameter collector
+//
+//==========================================================================
+extern int EncodeRegType(ExpEmit reg);
+
+class EmitterArray
+{
+	// std::function and TArray are not compatible so this has to use std::vector instead.
+	std::vector<std::function<int(VMFunctionBuilder *)>> emitters;
+	unsigned numparams = 0;
+
+public:
+	void AddParameter(VMFunctionBuilder *build, FxExpression *operand);
+	void AddParameter(ExpEmit &emit, bool reference);
+	void AddParameterPointerConst(void *konst);
+	void AddParameterPointer(int index, bool konst);
+	void AddParameterFloatConst(double konst);
+	void AddParameterIntConst(int konst);
+	void AddParameterStringConst(const FString &konst);
+	int EmitParameters(VMFunctionBuilder *build);
+	unsigned Count() const
+	{
+		return numparams;
+	}
+};
+
 #endif