It is possible. Dynamic code generation is even mainstream in some areas like software rendering and graphics. You find a lot of use in all kinds of script languages, in dynamic compilation of byte-code in machine code (.NET, Java, as far as I know Perl. Recently JavaScript joined the club as well).
You also find it used in very math-heavy applications as well, It makes a difference if you for example remove all multiplication with zero out of a matrix multiplication if you plan to do such a multiplication several thousand times.
I strongly suggest that you read on the SSA representation of code. That's a representation where each primitive is turned into the so called three operand form, and each variable is only assigned once (hence the same Static Single Assignment form).
You can run high order optimizations on such code, and it's straight forward to turn that code into executable code. You won't write that code-generation backend on a weekend though...
To get a feeling how the SSA looks like you can try out the LLVM compiler. On their web-site they have a little "Try Out" widget to play with. You paste C code into a window and you get something out that is close to the SSA form.
Little example how it looks like:
Lets take this integer square root algorithm in C. (arbitrary example, I just took something simple yet non-trivial):
unsigned int isqrt32 (unsigned int value)
{
unsigned int g = 0;
unsigned int bshift = 15;
unsigned int b = 1<<bshift;
do {
unsigned int temp = (g+g+b)<<bshift;
if (value >= temp) {
g += b;
value -= temp;
}
b>>=1;
} while (bshift--);
return g;
}
LLVM turns it into:
define i32 @isqrt32(i32 %value) nounwind {
entry:
br label %bb
bb: ; preds = %bb, %entry
%indvar = phi i32 [ 0, %entry ], [ %indvar.next, %bb ]
%b.0 = phi i32 [ 32768, %entry ], [ %tmp23, %bb ]
%g.1 = phi i32 [ 0, %entry ], [ %g.0, %bb ]
%value_addr.1 = phi i32 [ %value, %entry ], [ %value_addr.0, %bb ]
%bshift.0 = sub i32 15, %indvar
%tmp5 = shl i32 %g.1, 1
%tmp7 = add i32 %tmp5, %b.0
%tmp9 = shl i32 %tmp7, %bshift.0
%tmp12 = icmp ult i32 %value_addr.1, %tmp9
%tmp17 = select i1 %tmp12, i32 0, i32 %b.0
%g.0 = add i32 %tmp17, %g.1
%tmp20 = select i1 %tmp12, i32 0, i32 %tmp9
%value_addr.0 = sub i32 %value_addr.1, %tmp20
%tmp23 = lshr i32 %b.0, 1
%indvar.next = add i32 %indvar, 1
%exitcond = icmp eq i32 %indvar.next, 16
br i1 %exitcond, label %bb30, label %bb
bb30: ; preds = %bb
ret i32 %g.0
}
I know it looks horrible at first. It's not even pure SSA-Form. The more you read on that representation the more sense it will make. And you will also find out why this representation is so widely used these days.
Encapsulating all the info you need into a data-structure is easy. In the end you have to decide if you want to use enums or strings for opcode names ect.
Btw - I know I didn't gave you a data-structure but more a formal yet practical language and the advice where to look further.
It's a very nice and interesting research field.
Edit: And before I forget it: Don't overlook the built in features of .NET and Java. These languates allow you to compile from byte-code or source code from within the program and execute the result.
Cheers,
Nils
Regarding your edit: How to execute a binary blob with code:
Jumping into your binary blob is OS and platform dependent. In a nutshell you have invalide the instruction cache, maybe you have to writeback the data-cache and you may have to enable execution rights on the memory-region you've wrote your code into.
On win32 it's relative easy as instruction cache flushing seems to be sufficient if you place your code on the heap.
You can use this stub to get started:
typedef void (* voidfunc) (void);
void * generate_code (void)
{
// reserve some space
unsigned char * buffer = (unsigned char *) malloc (1024);
// write a single RET-instruction
buffer[0] = 0xc3;
return buffer;
}
int main (int argc, char **args)
{
// generate some code:
voidfunc func = (voidfunc) generate_code();
// flush instruction cache:
FlushInstructionCache(GetCurrentProcess(), func, 1024);
// execute the code (it does nothing atm)
func();
// free memory and exit.
free (func);
}