Last active
July 4, 2023 21:25
-
-
Save AMalahov/7c8df89e2dfee956fd2577073981a22d to your computer and use it in GitHub Desktop.
asm-like C++ (for https://stackoverflow.com/a/42145840/264047)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <cstdio> | |
#include <cassert> | |
// | |
// init | |
// | |
const int STACK_SIZE = 1000; | |
thread_local int stack[STACK_SIZE]; | |
int BP; | |
int SP; | |
int AX, BX; | |
// | |
// utility | |
// | |
void printRegisters(char* msg) { | |
std::cout << msg << ": " << "BP=" << BP << ", SP=" << SP << ", AX=" << AX << std::endl; | |
} | |
// | |
// "system" functions | |
// | |
void threadSetup() { | |
//stack = new int[STACK_SIZE]; | |
BP = SP = STACK_SIZE; // these will index beyond stack's end, but that's ok, because we decrement before first use | |
} | |
void threadTearDown() { | |
//delete[] stack; | |
} | |
void push(int value) { | |
--SP; | |
stack[SP] = value; | |
} | |
void pop(int& result) { | |
result = stack[SP]; | |
++SP; | |
} | |
// | |
// actual code | |
// | |
int triple(int a) { | |
int result = a * 3; | |
return result; | |
} | |
int triple_noLocals(int a) { | |
SP -= 1; // move pointer to unused location, where we can store what we need | |
stack[SP] = a * 3; | |
return stack[SP]; | |
} | |
int triple_noL_noParams() { // `a` is at index 999, SP == 999 | |
SP -= 1; // SP == 998, stack[SP + 1] == a | |
stack[SP] = stack[SP + 1] * 3; | |
return stack[SP]; | |
} | |
void triple_noL_noP_noReturn() { // `a` at 998, SP == 998 | |
SP -= 1; // SP == 997 | |
stack[SP] = stack[SP + 1] * 3; | |
AX = stack[SP]; | |
SP += 1; // finally we can cleanup locals right in the function body, SP == 998 | |
} | |
void triple_noLPR_withAnchor() { | |
push(BP); | |
BP = SP; | |
SP -= 1; | |
stack[BP - 1] = stack[BP + 1] * 3; | |
AX = stack[BP - 1]; | |
SP = BP; | |
pop(BP); | |
} | |
int myAlgo(int a, int b) { | |
int t1 = a * 3; | |
int t2 = b * 3; | |
return t1 - t2; | |
} | |
void myAlgo_noLPR() { // `a` is at index 997, `b` at 998, SP == 997 | |
SP -= 2; // SP == 995 | |
stack[SP + 1] = stack[SP + 2] * 3; | |
stack[SP] = stack[SP + 3] * 3; | |
AX = stack[SP + 1] - stack[SP]; | |
SP += 2; // cleanup locals, SP == 997 | |
} | |
void myAlgo_noLPR_withAnchor() { // `a` at 997, `b` at 998, SP == 997 | |
push(BP); // SP == 996 | |
BP = SP; // create anchor, stack[BP] == old value of BP, now BP == 996 | |
SP -= 2; // SP == 994 | |
stack[BP - 1] = stack[BP + 1] * 3; | |
stack[BP - 2] = stack[BP + 2] * 3; | |
AX = stack[BP - 1] - stack[BP - 2]; | |
SP = BP; // cleanup locals, SP == 996 | |
pop(BP); // SP == 997 | |
} | |
int myAlgo_withCalls(int a, int b) { | |
int t1 = triple(a); | |
int t2 = triple(b); | |
return t1 - t2; | |
} | |
// pushes the address of the code at label's location on the stack | |
// NOTE: this gonna work only with 32-bit compiler (so that pointer is 32-bit and fits in int) | |
#define PUSH_ADDRESS(labelName) { \ | |
void* tmpPointer; \ | |
__asm{ mov [tmpPointer], offset labelName } \ | |
push(reinterpret_cast<int>(tmpPointer)); \ | |
} | |
// why we need indirection, read http://stackoverflow.com/a/13301627/264047 | |
#define TOKENPASTE(x, y) x ## y | |
#define TOKENPASTE2(x, y) TOKENPASTE(x, y) | |
// generates token (not a string) we will use as label name. | |
// Example: LABEL_NAME(155) will generate token `lbl_155` | |
#define LABEL_NAME(num) TOKENPASTE2(lbl_, num) | |
#define CALL_IMPL(funcLabelName, callId) \ | |
PUSH_ADDRESS(LABEL_NAME(callId)); \ | |
goto funcLabelName; \ | |
LABEL_NAME(callId) : | |
// saves return address on the stack and jumps to label `funcLabelName` | |
#define CALL(funcLabelName) CALL_IMPL(funcLabelName, __LINE__) | |
// takes address at the top of stack and jump there | |
#define RET() { \ | |
int tmpInt; \ | |
pop(tmpInt); \ | |
void* tmpPointer = reinterpret_cast<void*>(tmpInt); \ | |
__asm{ jmp tmpPointer } \ | |
} | |
void myAlgo_asm() { | |
goto my_algo_start; | |
triple_label: | |
push(BP); | |
BP = SP; | |
SP -= 1; | |
// stack[BP] == old BP, stack[BP + 1] == return address | |
stack[BP - 1] = stack[BP + 2] * 3; | |
AX = stack[BP - 1]; | |
SP = BP; | |
pop(BP); | |
RET(); | |
my_algo_start: | |
push(BP); // SP == 995 | |
BP = SP; // BP == 995; stack[BP] == old BP, stack[BP + 1] == dummy return address, `a` at [BP + 2], `b` at [BP + 3] | |
SP -= 2; // SP == 993 | |
push(AX); | |
push(stack[BP + 2]); | |
CALL(triple_label); | |
stack[BP - 1] = AX; | |
SP -= 1; | |
pop(AX); | |
push(AX); | |
push(stack[BP + 3]); | |
CALL(triple_label); | |
stack[BP - 2] = AX; | |
SP -= 1; | |
pop(AX); | |
AX = stack[BP - 1] - stack[BP - 2]; | |
SP = BP; // cleanup locals, SP == 997 | |
pop(BP); | |
} | |
int main() { | |
threadSetup(); | |
int original = triple(11); | |
std::cout << "triple original: "<< original << std::endl; | |
assert(original == triple_noLocals(11)); | |
// now SP == 999, but we don't need the value at stack[999] anymore | |
// and we will move the stack index back, so we can reuse it later | |
SP += 1; // SP == 1000 | |
printRegisters("no locals"); | |
push(11); // SP == 999 | |
assert(original == triple_noL_noParams()); | |
SP += 2; // cleanup 1 local and 1 parameter | |
printRegisters("no l, no p"); | |
push(AX); // SP == 999 | |
push(11); // SP == 998 | |
triple_noL_noP_noReturn(); | |
assert(original == AX); | |
printRegisters("no LPR, before cleanup"); | |
SP += 1; // cleanup param | |
// locals were cleaned up in the function body, so we don't need to do it here | |
pop(AX); // restore AX | |
printRegisters("no LPR, after cleanup"); | |
push(AX); | |
push(11); | |
triple_noLPR_withAnchor(); | |
assert(original == AX); | |
printRegisters("no LPR w/ A, before cleanup"); | |
SP += 1; | |
pop(AX); | |
printRegisters("no LPR w/ A, after cleanup"); | |
original = myAlgo(11, 22); | |
std::cout << std::endl << "myAlgo original: " << original << std::endl; | |
push(AX); // SP == 999 | |
push(22); // SP == 998 | |
push(11); // SP == 997 | |
myAlgo_noLPR(); | |
assert(original == AX); | |
printRegisters("no LPR, before cleanup"); | |
SP += 2; | |
pop(AX); | |
printRegisters("no LPR, after cleanup"); | |
push(AX); | |
push(22); | |
push(11); | |
myAlgo_noLPR_withAnchor(); | |
assert(original == AX); | |
printRegisters("no LPR w/ A, before cleanup"); | |
SP += 2; | |
pop(AX); | |
printRegisters("no LPR w/ A, after cleanup"); | |
assert(original == myAlgo_withCalls(11, 22)); | |
push(AX); | |
push(22); | |
push(11); | |
push(7777); // dummy value, so that offsets inside function are like we've pushed return address | |
myAlgo_asm(); | |
assert(original == AX); | |
printRegisters("asm, before cleanup"); | |
SP += 1; // pop dummy "return address" | |
SP += 2; | |
pop(AX); | |
printRegisters("asm, after cleanup"); | |
getchar(); | |
threadTearDown(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment