# -*- C -*-
# bytecode.def - definitions of bytecodes for the stack machine.

# The production of the bytecode interpreter and compiler is
# heavily automated by using this file creatively.

# Various elementary data types are understood by the bytecode interpreter.
# Q[IU] - quarter word (byte) signed and unsigned integers (char).
# H[IU] - half word signed and unsigned integers (short int, maybe int).
# S[IU] - single word signed and unsigned integers (maybe int, long int).
# D[IU] - double word signed and unsigned integers (long long int).
# SF - single precision floating point (float).
# DF - double precision floating point (double).
# XF - extended precision floating point (long double).
# P - pointer type for address arithmetic and other purposes.

# The bytecode specification consists of a series of define_operator
# forms, that are parsed by preprocessors to automatically build
# various switch statements.
#	define_operator(name,
#			<C prototype code for implementing the operator>,
#			<list of variations>)
# The <C prototype> is self explanatory.
# The <list of variations> consists of a (parenthesized list) of
# variation items, each of which is in itself a list.  A variation
# item consists of a name suffix, the types of the input arguments
# expected on the stack (shallowest item first) and (optionally) the
# types of the output arguments (similarly ordered).  Finally, the
# types of the literal arguments (if any) may appear.

# Substitution in the C prototype code is as follows:
# Substitution happens only after a dollar sign.  To get a literal
# dollar sign (why would you ever want one anyway?) use $$.
# $R1 means "result 1" $TR1 means "type name of result one"
# $S1 means "source 1" and similarly with $TS1.
# $L1 means "literal (inline) argument 1" and $TL1 means type thereof.
#

# Notice that the number following $R doesn't affect the push order;
# it's used only for clarity and orthogonality, although it's checked
# to make sure it doesn't exceed the number of outputs. A $R reference
# results in a push, and represents the result lvalue. E.g.

#	$R1 = 2\, $R2 = 17
# will expand to:
#	INTERP_PUSH($TR1) = 2, INTERP_PUSH($TR2) = 17
#

# Opcode 0 should never happen.
define_operator(neverneverland, abort\(\), (()))

# Stack manipulations.
define_operator(drop, 0, ((, (SI))))
define_operator(dup, 0, ((, (SI), (SI, SI))))
define_operator(over, 0, ((, (SI), (SI, SI))))

# Adjust stack pointer

define_operator(setstack, 0, ((SI,,,(SI))))
define_operator(adjstack, 0, ((SI,,,(SI))))

# Constants, loads, and stores.
define_operator(const,
		$R1 = $L1,
		((QI,, (QI), (QI)), (HI,, (HI), (HI)),
		 (SI,, (SI), (SI)), (DI,, (DI), (DI)),
		 (SF,, (SF), (SF)), (DF,, (DF), (DF)),
		 (XF,, (XF), (XF)), (P,, (P), (P))))
define_operator(load,
		$R1 = *\($TR1 *\) $S1,
		((QI, (P), (QI)), (HI, (P), (HI)),
		 (SI, (P), (SI)), (DI, (P), (DI)),
		 (SF, (P), (SF)), (DF, (P), (DF)),
		 (XF, (P), (XF)), (P, (P), (P))))
define_operator(store,
		*\($TS2 *\) $S1 = $S2,
		((QI, (P, QI)), (HI, (P, HI)),
		 (SI, (P, SI)), (DI, (P, DI)),
		 (SF, (P, SF)), (DF, (P, DF)),
		 (XF, (P, XF)), (P, (P, P)),
		 (BLK, (SI, BLK, BLK))))

# Clear memory block

define_operator(clear, $S1 + $S2, ((BLK, (SI, BLK))))


# Advance pointer by SI constant

define_operator(addconst, $R1 = $S1, ((PSI, (P), (P), (SI))))


# newlocalSI is used for creating variable-sized storage during function
# initialization.

# Create local space, return pointer to block

define_operator(newlocal, $R1 = $S1, ((SI, (SI), (P))))


# Push the address of a local variable.
define_operator(local, $R1 = locals + $L1, ((P,, (P), (SI))))

# Push the address of an argument variable.
define_operator(arg, $R1 = args + $L1, ((P,, (P), (SI))))

# Arithmetic conversions.
define_operator(convert,
		$R1 = \($TR1\) $S1,
		(# Signed integral promotions (sign extensions).
		 (QIHI, (QI), (HI)), (HISI, (HI), (SI)), (SIDI, (SI), (DI)),
		 (QISI, (QI), (SI)),
		 # Unsigned integral promotions (zero extensions).
		 (QUHU, (QU), (HU)), (HUSU, (HU), (SU)), (SUDU, (SU), (DU)),
		 (QUSU, (QU), (SU)),
		 # Floating promotions.
		 (SFDF, (SF), (DF)), (DFXF, (DF), (XF)),
		 # Integral truncation.
		 (HIQI, (HI), (QI)), (SIHI, (SI), (HI)), (DISI, (DI), (SI)),
		 (SIQI, (SI), (QI)),
                 # Unsigned truncation.
		 (SUQU, (SU), (QU)),
		 # Floating truncation.
		 (DFSF, (DF), (SF)), (XFDF, (XF), (DF)),
		 # Integral conversions to floating types.
		 (SISF, (SI), (SF)), (SIDF, (SI), (DF)), (SIXF, (SI), (XF)),
		 (SUSF, (SU), (SF)), (SUDF, (SU), (DF)), (SUXF, (SU), (XF)),
		 (DISF, (DI), (SF)), (DIDF, (DI), (DF)), (DIXF, (DI), (XF)),
		 (DUSF, (DU), (SF)), (DUDF, (DU), (DF)), (DUXF, (DU), (XF)),
		 # Floating conversions to integral types.
		 (SFSI, (SF), (SI)), (DFSI, (DF), (SI)), (XFSI, (XF), (SI)),
		 (SFSU, (SF), (SU)), (DFSU, (DF), (SU)), (XFSU, (XF), (SU)),
		 (SFDI, (SF), (DI)), (DFDI, (DF), (DI)), (XFDI, (XF), (DI)),
		 (SFDU, (SF), (DU)), (DFDU, (DF), (DU)), (XFDU, (XF), (DU)),
		 # Pointer/integer conversions.
		 (PSI, (P), (SI)), (SIP, (SI), (P))))

# Truth value conversion.  These are necessary because conversions of, e.g.,
# floating types to integers may not function correctly for large values.
define_operator(convert,
		$R1 = !!$S1,
		((SIT, (SI), (T)), (DIT, (DI), (T)),
		 (SFT, (SF), (T)), (DFT, (DF), (T)),
		 (XFT, (XF), (T)), (PT, (P), (T))))

# Bit field load/store.

# Load and zero-extend bitfield

define_operator(zxload, $R1 = $S1, ((BI, (SU, SU, P), (SU))))

# Load and sign-extend bitfield

define_operator(sxload, $R1 = $S1, ((BI, (SU, SU, P), (SI))))

# Store integer in bitfield

define_operator(sstore, $R1 = $S1, ((BI, (SU, SU, P, SI))))


# Binary operations.
define_operator(add,
		$R1 = $S1 + $S2,
		((SI, (SI, SI), (SI)), (DI, (DI, DI), (DI)),
		 (SF, (SF, SF), (SF)), (DF, (DF, DF), (DF)),
		 (XF, (XF, XF), (XF)),
		 (PSI, (P, SI), (P))))
define_operator(sub,
		$R1 = $S1 - $S2,
		((SI, (SI, SI), (SI)), (DI, (DI, DI), (DI)),
		 (SF, (SF, SF), (SF)), (DF, (DF, DF), (DF)),
		 (XF, (XF, XF), (XF)),
		 (PP, (P, P), (SI))))
define_operator(mul,
		$R1 = $S1 * $S2,
		((SI, (SI, SI), (SI)), (DI, (DI, DI), (DI)),
		 (SU, (SU, SU), (SU)), (DU, (DU, DU), (DU)),
		 (SF, (SF, SF), (SF)), (DF, (DF, DF), (DF)),
		 (XF, (XF, XF), (XF))))
define_operator(div,
		$R1 = $S1 / $S2,
		((SI, (SI, SI), (SI)), (DI, (DI, DI), (DI)),
		 (SU, (SU, SU), (SU)), (DU, (DU, DU), (DU)),
		 (SF, (SF, SF), (SF)), (DF, (DF, DF), (DF)),
		 (XF, (XF, XF), (XF))))
define_operator(mod,
		$R1 = $S1 % $S2,
		((SI, (SI, SI), (SI)), (DI, (DI, DI), (DI)),
		 (SU, (SU, SU), (SU)), (DU, (DU, DU), (DU))))
define_operator(and,
		$R1 = $S1 & $S2,
		((SI, (SI, SI), (SI)), (DI, (DI, DI), (DI))))
define_operator(ior,
		$R1 = $S1 | $S2,
		((SI, (SI, SI), (SI)), (DI, (DI, DI), (DI))))
define_operator(xor,
		$R1 = $S1 ^ $S2,
		((SI, (SI, SI), (SI)), (DI, (DI, DI), (DI))))
define_operator(lshift,
		$R1 = $S1 << $S2,
		((SI, (SI, SI), (SI)), (SU, (SU, SI), (SU)),
		 (DI, (DI, SI), (DI)), (DU, (DU, SI), (DU))))
define_operator(rshift,
		$R1 = $S1 >> $S2,
		((SI, (SI, SI), (SI)), (SU, (SU, SI), (SU)),
		 (DI, (DI, SI), (DI)), (DU, (DU, SI), (DU))))
define_operator(lt,
		$R1 = $S1 < $S2,
		((SI, (SI, SI), (T)), (SU, (SU, SU), (T)),
		 (DI, (DI, DI), (T)), (DU, (DU, DU), (T)),
		 (SF, (SF, SF), (T)), (DF, (DF, DF), (T)),
		 (XF, (XF, XF), (T)), (P, (P, P), (T))))
define_operator(le,
		$R1 = $S1 <= $S2,
		((SI, (SI, SI), (T)), (SU, (SU, SU), (T)),
		 (DI, (DI, DI), (T)), (DU, (DU, DU), (T)),
		 (SF, (SF, SF), (T)), (DF, (DF, DF), (T)),
		 (XF, (XF, XF), (T)), (P, (P, P), (T))))
define_operator(ge,
		$R1 = $S1 >= $S2,
		((SI, (SI, SI), (T)), (SU, (SU, SU), (T)),
		 (DI, (DI, DI), (T)), (DU, (DU, DU), (T)),
		 (SF, (SF, SF), (T)), (DF, (DF, DF), (T)),
		 (XF, (XF, XF), (T)), (P, (P, P), (T))))
define_operator(gt,
		$R1 = $S1 > $S2,
		((SI, (SI, SI), (T)), (SU, (SU, SU), (T)),
		 (DI, (DI, DI), (T)), (DU, (DU, DU), (T)),
		 (SF, (SF, SF), (T)), (DF, (DF, DF), (T)),
		 (XF, (XF, XF), (T)), (P, (P, P), (T))))
define_operator(eq,
		$R1 = $S1 == $S2,
		((SI, (SI, SI), (T)), (DI, (DI, DI), (T)),
		 (SF, (SF, SF), (T)), (DF, (DF, DF), (T)),
		 (XF, (XF, XF), (T)), (P, (P, P), (T))))
define_operator(ne,
		$R1 = $S1 != $S2,
		((SI, (SI, SI), (T)), (DI, (DI, DI), (T)),
		 (SF, (SF, SF), (T)), (DF, (DF, DF), (T)),
		 (XF, (XF, XF), (T)), (P, (P, P), (T))))

# Unary operations.
define_operator(neg,
		$R1 = -$S1,
		((SI, (SI), (SI)), (DI, (DI), (DI)),
		 (SF, (SF), (SF)), (DF, (DF), (DF)),
		 (XF, (XF), (XF))))
define_operator(not,
		$R1 = ~$S1,
		((SI, (SI), (SI)), (DI, (DI), (DI))))
define_operator(not,
		$R1 = !$S1,
		((T, (SI), (SI))))

# Increment operations.
define_operator(predec,
		$R1 = *\($TR1 *\) $S1 -= $S2,
		((QI, (P, QI), (QI)), (HI, (P, HI), (HI)),
		 (SI, (P, SI), (SI)), (DI, (P, DI), (DI)),
		 (P, (P, SI), (P)), (SF, (P, SF), (SF)),
		 (DF, (P, DF), (DF)), (XF, (P, XF), (XF)),
		 (BI, (SU, SU, P, SI), (SI))))

define_operator(preinc,
		$R1 = *\($TR1 *\) $S1 += $S2,
		((QI, (P, QI), (QI)), (HI, (P, HI), (HI)),
		 (SI, (P, SI), (SI)), (DI, (P, DI), (DI)),
		 (P, (P, SI), (P)), (SF, (P, SF), (SF)),
		 (DF, (P, DF), (DF)), (XF, (P, XF), (XF)),
		 (BI, (SU, SU, P, SI), (SI))))

define_operator(postdec,
		$R1 = *\($TR1 *\) $S1\, *\($TR1 *\) $S1 -= $S2,
		((QI, (P, QI), (QI)), (HI, (P, HI), (HI)),
		 (SI, (P, SI), (SI)), (DI, (P, DI), (DI)),
		 (P, (P, SI), (P)), (SF, (P, SF), (SF)),
		 (DF, (P, DF), (DF)), (XF, (P, XF), (XF)),
		 (BI, (SU, SU, P, SI), (SI))))

define_operator(postinc,
		$R1 = *\($TR1 *\) $S1\, *\($TR1 *\) $S1 += $S2,
		((QI, (P, QI), (QI)), (HI, (P, HI), (HI)),
		 (SI, (P, SI), (SI)), (DI, (P, DI), (DI)),
		 (P, (P, SI), (P)), (SF, (P, SF), (SF)),
		 (DF, (P, DF), (DF)), (XF, (P, XF), (XF)),
		 (BI, (SU, SU, P, SI), (SI))))

# Jumps.
define_operator(xjumpif, if \($S1\) pc = code->pc0 + $L1, ((, (T),, (SI))))
define_operator(xjumpifnot, if \(! $S1\) pc = code->pc0 + $L1, ((, (T),, (SI))))
define_operator(jump, pc = code->pc0 + $L1, ((,,,(SI))))

# This is for GCC2. It jumps to the address on the stack.
define_operator(jump, pc = \(void *\) $S1, ((P,,)))

# Switches.  In order to (eventually) support ranges we provide four different
# varieties of switches.  Arguments are the switch index from the stack, the
# bytecode offset of the switch table, the size of the switch table, and 
# the default label.
define_operator(caseSI, CASESI\($S1\, $L1\, $L2\, $L3\), ((, (SI),, (SI, SI, SI))))
define_operator(caseSU, CASESU\($S1\, $L1\, $L2\, $L3\), ((, (SU),, (SI, SI, SI))))
define_operator(caseDI, CASEDI\($S1\, $L1\, $L2\, $L3\), ((, (DI),, (SI, SI, SI))))
define_operator(caseDU, CASEDU\($S1\, $L1\, $L2\, $L3\), ((, (DU),, (SI, SI, SI))))

# Procedure call.
# Stack arguments are (deepest first):
#	procedure arguments in reverse order.
#	pointer to the place to hold the return value.
#	address of the call description vector.
#	pointer to the procedure to be called.
define_operator(call, CALL\($S1\, $S2\, $S3\, sp\), ((, (P, P, P))))

# Procedure return.
# Pushes on interpreter stack:
#       value of retptr (pointer to return value storage slot)
define_operator(return, $R1 = retptr, ((P,,(P))))

# Really return.
define_operator(ret, return, (()))

# Print an obnoxious line number.
define_operator(linenote, fprintf\(stderr\, "%d\\n"\, $L1\), ((,,,(SI))))
