Skip to content

Commit 775290a

Browse files
committed
Implemented labels and jumps, but will have to resolve arguments during a later compilation stage to allow skipping instructions by jumping to a label that is not yet declared.
1 parent 771ed52 commit 775290a

File tree

3 files changed

+75
-6
lines changed

3 files changed

+75
-6
lines changed

Source/DFPSR/api/mediaMachineAPI.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,14 @@ static const InsSig<MEDIA_MACHINE_TYPE_COUNT> mediaMachineInstructions[] = {
164164
},
165165
ArgSig(U"Target", false, DataType_ImageRgbaU8)
166166
),
167+
InsSig<MEDIA_MACHINE_TYPE_COUNT>::create(U"JUMP", 1,
168+
[](VirtualMachine<MEDIA_MACHINE_TYPE_COUNT>& machine, PlanarMemory<MEDIA_MACHINE_TYPE_COUNT>& memory, const List<VMA>& args) {
169+
int32_t targetAddress = args[0].index;
170+
// TODO: Assert that the target address is within the same method when running in debug mode.
171+
memory.current.programCounter = targetAddress;
172+
},
173+
ArgSig(U"InstructionAddress", true, DataType_InstructionAddress)
174+
),
167175
InsSig<MEDIA_MACHINE_TYPE_COUNT>::create(U"ROUND", 1,
168176
[](VirtualMachine<MEDIA_MACHINE_TYPE_COUNT>& machine, PlanarMemory<MEDIA_MACHINE_TYPE_COUNT>& memory, const List<VMA>& args) {
169177
SCALAR_REF(0) = FixedPoint::fromWhole(fixedPoint_round(SCALAR_VALUE(1)));

Source/DFPSR/implementation/machine/VirtualMachine.h

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,18 @@ static ReadableString getName(AccessType access) {
8282

8383
// Types used in machine instuctions
8484
enum class ArgumentType {
85-
Immediate, // Index to MemoryPlane::immediate
86-
Reference, // Indirect index translated into both global and stack-relative locations within MemoryPlane::stack
87-
MethodReference // Index to a method to call
85+
Immediate, // Index to MemoryPlane::immediate
86+
Reference, // Indirect index translated into both global and stack-relative locations within MemoryPlane::stack
87+
MethodReference, // Index to a method to call
88+
InstructionAddress // Index to a machine instruction, which instructions can jump to.
8889
};
8990

9091
// Types
91-
// TODO: Make the use of FixedPoint optional in VirtualMachine
9292
using DataType = int32_t;
9393

94+
// Instruction addresses uses the list of instructions as their read-only memory.
95+
static const DataType DataType_InstructionAddress = -1;
96+
9497
template <int32_t TYPE_COUNT>
9598
struct Variable {
9699
String name;
@@ -129,9 +132,9 @@ struct ArgSig {
129132
: name(name), byValue(byValue), dataType(dataType) {}
130133
bool matches(ArgumentType argType, DataType dataType) const {
131134
if (this->byValue) {
132-
return dataType == this->dataType && (argType == ArgumentType::Immediate || argType == ArgumentType::Reference);
135+
return (dataType == this->dataType) && (argType == ArgumentType::Reference || argType == ArgumentType::Immediate || argType == ArgumentType::InstructionAddress);
133136
} else {
134-
return dataType == this->dataType && argType == ArgumentType::Reference;
137+
return (dataType == this->dataType) && (argType == ArgumentType::Reference);
135138
}
136139
}
137140
};
@@ -276,6 +279,13 @@ struct VMTypeDef {
276279
: name(name), dataType(dataType), allowDefaultValue(allowDefaultValue), debugPrinter(debugPrinter) {}
277280
};
278281

282+
struct InstructionLabel {
283+
int32_t address;
284+
String identifier;
285+
InstructionLabel(int32_t address, const ReadableString &identifier)
286+
: address(address), identifier(identifier) {}
287+
};
288+
279289
template <int32_t TYPE_COUNT>
280290
struct Method {
281291
String name;
@@ -293,6 +303,10 @@ struct Method {
293303
bool declaredLocals = false; // Goes true when a local is declared
294304
List<Variable<TYPE_COUNT>> locals; // locals[0..inputCount-1] are the inputs, while locals[inputCount..inputCount+outputCount-1] are the outputs
295305

306+
bool ended = false;
307+
308+
List<InstructionLabel> labels;
309+
296310
// Type-specific spaces
297311
FixedArray<int32_t, TYPE_COUNT> count;
298312
// Look-up table from a combination of type and type-local indices to unified-local indices
@@ -509,6 +523,15 @@ struct VirtualMachine {
509523
});
510524
}
511525

526+
bool isInsideOfMethod() const {
527+
return this->methods.length() > 0 && !(this->methods.last().ended);
528+
}
529+
530+
void endCurrentMethod() {
531+
this->addReturnInstruction();
532+
this->methods.last().ended = true;
533+
}
534+
512535
static ReadableString getArg(const List<String>& arguments, int32_t index) {
513536
if (index < 0 || index >= arguments.length()) {
514537
return U"";
@@ -781,6 +804,19 @@ struct VirtualMachine {
781804
throwError(U"Using > without < for in-place temp allocation.\n");
782805
return VMA(ArgumentType::Immediate, -1, -1);
783806
} else {
807+
// Look for a label.
808+
if (this->isInsideOfMethod()) {
809+
Method<TYPE_COUNT> &method = this->methods.last();
810+
String labelName = string_removeOuterWhiteSpace(content);
811+
debugText(U"Checking if ", labelName, U" is a label in the ", method.name, U" method.\n");
812+
for (int32_t l = 0; l < method.labels.length(); l++) {
813+
debugText(U" * Label ", method.labels[l].identifier, U" @ ", method.labels[l].address, U"\n");
814+
if (string_caseInsensitiveMatch(method.labels[l].identifier, labelName)) {
815+
return VMA(ArgumentType::InstructionAddress, DataType_InstructionAddress, method.labels[l].address);
816+
}
817+
}
818+
}
819+
// If it is not a label then look for variables.
784820
Variable<TYPE_COUNT>* resource = getResource(content, methodIndex);
785821
if (resource) {
786822
return VMA(ArgumentType::Reference, resource->typeDescription->dataType, resource->getGlobalIndex());
@@ -808,6 +844,7 @@ struct VirtualMachine {
808844
if (this->methods.length() == 1) {
809845
// When more than one function exists, the init method must end with a return instruction
810846
// Otherwise it would start executing instructions in another method and crash
847+
// Finalizing the init method must wait until the next method begins, because it is extended each time a global variable is introduced at the top.
811848
this->addReturnInstruction();
812849
}
813850
this->methods.pushConstruct(getArg(arguments, 0), this->machineWords.length(), this->machineTypeCount);
@@ -822,10 +859,31 @@ struct VirtualMachine {
822859
} else if (string_caseInsensitiveMatch(command, U"Output")) {
823860
this->declareVariable(methods.length() - 1, AccessType::Output, getArg(arguments, 0), getArg(arguments, 1), true, getArg(arguments, 2));
824861
} else if (string_caseInsensitiveMatch(command, U"End")) {
862+
if (!this->isInsideOfMethod()) throwError(U"Can not end a method outside of methods!\n");
825863
this->addReturnInstruction();
826864
} else if (string_caseInsensitiveMatch(command, U"Call")) {
865+
if (!this->isInsideOfMethod()) throwError(U"Can not make calls outside of methods!\n");
827866
this->addCallInstructions(arguments);
867+
} else if (string_caseInsensitiveMatch(command, U"Label")) {
868+
// TODO: Move this into a method for creating a label.
869+
// TODO: Prevent overlapping names between methods, local variables and local labels.
870+
// TODO: Resolve label identifiers into ArgumentType::InstructionAddress and assign the argument's index directly to the program counter when jumping.
871+
if (!this->isInsideOfMethod()) {
872+
throwError(U"Can not place labels outside of methods!\n");
873+
} else if (arguments.length() < 1) {
874+
throwError(U"Labels need to be named to allow identifying the following machine instruction!\n");
875+
} else if (arguments.length() > 1) {
876+
throwError(U"Labels may not have more than one argument containing the identifier!\n");
877+
} else {
878+
int32_t targetAddress = this->machineWords.length();
879+
String labelIdentifier = arguments[0];
880+
// TODO: Reject invalid label names.
881+
debugText(U"Declaring label ", labelIdentifier, U" @ ", targetAddress, U"\n");
882+
this->methods.last().labels.pushConstruct(targetAddress, labelIdentifier);
883+
}
828884
} else {
885+
// TODO: Delay resolving so that functions can be declared after calls to them and jumps can go to later labels.
886+
// If the command did not match with any of the generic instructions, resolve the arguments and look for a custom machine instruction.
829887
int32_t methodIndex = this->methods.length() - 1;
830888
List<VMA> resolvedArguments;
831889
for (int32_t a = 0; a < arguments.length(); a++) {

Source/test/tests/MediaMachineTest.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ START_TEST(CompilerFront)
1515
U" INPUT: FixedPoint, x\n"
1616
U" OUTPUT: FixedPoint, result\n"
1717
U" CALL: addFixedPoint, result, x, 8\n"
18+
U" JUMP: myLabel\n"
19+
U" MOVE: result, 10\n" // Dead code to jump past
20+
U" LABEL: myLabel\n"
1821
U"END:\n"
1922
);
2023
ASSERT(mediaMachine_exists(testMachine));

0 commit comments

Comments
 (0)