cvsuser 03/02/17 05:56:38
Modified: languages/imcc imc.c imc.h imcc.l imcc.y instructions.c
main.c optimizer.c parser_util.c pbc.c symreg.c
languages/imcc/t/imcpasm opt1.t
Log:
imcc-opt: J.Boemmels patch #21033; much more optimizations
Revision Changes Path
1.33 +11 -0 parrot/languages/imcc/imc.c
Index: imc.c
===================================================================
RCS file: /cvs/public/parrot/languages/imcc/imc.c,v
retrieving revision 1.32
retrieving revision 1.33
diff -u -w -r1.32 -r1.33
--- imc.c 8 Feb 2003 17:16:16 -0000 1.32
+++ imc.c 17 Feb 2003 13:56:36 -0000 1.33
@@ -23,6 +23,17 @@
static IMCStack nodeStack;
+/* return the index of a PMC class */
+int get_pmc_num(struct Parrot_Interp *interp, char *pmc_type)
+{
+ STRING * s = string_make(interp, pmc_type,
+ (UINTVAL) strlen(pmc_type), NULL, 0, NULL);
+ PMC * key = key_new_string(interp, s);
+ PMC * cnames = interp->Parrot_base_classname_hash;
+
+ return cnames->vtable->get_integer_keyed(interp, cnames, key);
+}
+
/* allocate is the main loop of the allocation algorithm */
void allocate(struct Parrot_Interp *interpreter) {
int to_spill;
1.26 +1 -0 parrot/languages/imcc/imc.h
Index: imc.h
===================================================================
RCS file: /cvs/public/parrot/languages/imcc/imc.h,v
retrieving revision 1.25
retrieving revision 1.26
diff -u -w -r1.25 -r1.26
--- imc.h 8 Feb 2003 14:41:28 -0000 1.25
+++ imc.h 17 Feb 2003 13:56:36 -0000 1.26
@@ -54,6 +54,7 @@
void restore_interference_graph(void);
void free_reglist(void);
int neighbours(int node);
+int get_pmc_num(struct Parrot_Interp *interp, char *pmc_type);
int check_op(struct Parrot_Interp *, char * fullname, char *op, SymReg *r[]);
1.27 +459 -61 parrot/languages/imcc/imcc.l
Index: imcc.l
===================================================================
RCS file: /cvs/public/parrot/languages/imcc/imcc.l,v
retrieving revision 1.26
retrieving revision 1.27
diff -u -w -r1.26 -r1.27
--- imcc.l 11 Feb 2003 11:34:36 -0000 1.26
+++ imcc.l 17 Feb 2003 13:56:36 -0000 1.27
@@ -16,10 +16,56 @@
#include "imc.h"
#include "parser.h"
-#define YY_NO_UNPUT
+#define MAX_PARAM 16
+
+struct params_t {
+ char *name[MAX_PARAM];
+ int num_param;
+};
+
+struct macro_t {
+ char *name;
+ struct params_t params;
+ char *expansion;
+};
+
+/* XXX: boe: rework this hack to use a hash */
+struct macro_t macros[256];
+int num_macros = 0;
+
+char temp_buffer[4096];
+
+struct macro_frame_t {
+ struct macro_frame_t *next;
+ YY_BUFFER_STATE buffer;
+ struct params_t *params;
+ struct params_t expansion;
+ int label;
+};
+
+struct macro_frame_t *frames = NULL;
+
+/* static function declariations */
+static struct macro_frame_t *new_frame (void);
+/*static void add_param_to_frame (const char *param, const char *expansion); */
+static void scan_string (struct macro_frame_t *frame, const char *expansion);
+static void scan_file (struct macro_frame_t *frame, FILE *);
+static void destroy_frame (struct macro_frame_t *frame);
+static int yylex_skip (YYSTYPE *valp, void *interp, const char *skip);
+
+static int read_macro (YYSTYPE *valp, void *interp);
+static int expand_macro (YYSTYPE *valp, void *interp, const char *name);
+static void include_file (const char *file_name);
+
#define YY_DECL int yylex(YYSTYPE *valp, struct Parrot_Interp *interp)
-int state;
+#define YYCHOP() (yytext[--yyleng] = '\0')
+#define DUP_AND_RET(valp, token) \
+ do { \
+ if (valp) (valp)->s = str_dup(yytext); \
+ return token; \
+ } while (0)
+
%}
%option outfile="imclexer.c"
@@ -32,19 +78,23 @@
SIGN [-+]
FLOATNUM {SIGN}?{DIGIT}+{DOT}{DIGIT}*([eE]{SIGN}?{DIGIT}+)?
LETTERDIGIT [a-zA-Z0-9_]
+ID {LETTER}{LETTERDIGIT}*
STRINGCONSTANT \"(\\\"|[^"\n]*)*\"
CHARCONSTANT \'[^'\n]*\'
RANKSPEC \[[,]*\]
EOL \r?\n
+WS [\t\f\r ]
%x emit
+%x macro
+
%%
- /* for emacs: ' */
+ /* for emacs "*/
if (expect_pasm == 1) {
expect_pasm = 2;
BEGIN(emit);
}
- if (pasm_file && YYSTATE != emit) {
+ if (pasm_file && YYSTATE == INITIAL) {
if (pasm_file == 1) {
BEGIN(emit);
return EMIT;
@@ -56,7 +106,7 @@
if (expect_pasm == 2)
BEGIN(INITIAL);
expect_pasm = 0;
- line++;
+ if (frames) line++;
return '\n';
}
@@ -128,17 +178,68 @@
"!=" return(RELOP_NE);
"**" return(POW);
-<emit,INITIAL>"," return(COMMA);
+<emit>".macro" {
+ return read_macro(valp, interp);
+ }
-<emit,INITIAL>{LETTER}{LETTERDIGIT}*":" {
- yytext[yyleng-1] = 0; /* trim last ':' */
- valp->s = str_dup(yytext);
- return(LABEL);
+<emit>".include" {
+ int c;
+
+ c = yylex(valp, interp);
+ if (c != STRINGC) return c;
+
+ YYCHOP();
+ include_file(str_dup(yytext + 1));
}
-<emit>{DOT}{LETTER}{LETTERDIGIT}* {
- valp->s = str_dup(yytext+1);
- return(MACRO);
+<emit>{ID}"$:" {
+ char *label;
+
+ if (valp) {
+ YYCHOP(); YYCHOP();
+
+ label = mem_sys_allocate(yyleng+10);
+ sprintf(label, "%s%d", yytext, frames->label);
+
+ valp->s = label;
+ }
+
+ return LABEL;
+ }
+
+<emit>{ID}"$" {
+ char *label;
+
+ if (valp) {
+ YYCHOP();
+
+ label = mem_sys_allocate(yyleng+10);
+ sprintf(label, "%s%d", yytext, frames->label);
+
+ valp->s = label;
+ }
+
+ return IDENTIFIER;
+ }
+
+<emit,INITIAL>"," return(COMMA);
+
+<emit,INITIAL>{ID}":" {
+ YYCHOP(); /* trim last ':' */
+ DUP_AND_RET(valp,LABEL);
+ }
+
+<emit,INITIAL>{DOT}{LETTER}{LETTERDIGIT}* {
+ int type = get_pmc_num(interp, yytext+1);
+
+ if (type) {
+ char *buf = malloc(16);
+ sprintf(buf, "%d", type);
+ valp->s = buf;
+ return INTC;
+ }
+ if (!expand_macro(valp, interp, yytext+1))
+ fataly(1, "", line, "unknown macro '%s'\n", yytext);
}
<emit,INITIAL>{LETTER}{LETTERDIGIT}* {
@@ -153,53 +254,28 @@
return(is_op(interp, valp->s) ? PARROT_OP : IDENTIFIER);
}
-<emit,INITIAL>{FLOATNUM} {
- valp->s = str_dup(yytext);
- return(FLOATC);
- }
+<*>{FLOATNUM} DUP_AND_RET(valp, FLOATC);
+<*>{SIGN}?{DIGIT}+ DUP_AND_RET(valp, INTC);
+<*>{HEX} DUP_AND_RET(valp, INTC);
+<*>{BIN} DUP_AND_RET(valp, INTC);
-<emit,INITIAL>{SIGN}?{DIGIT}+ {
+<*>{STRINGCONSTANT} {
valp->s = str_dup(yytext);
- return(INTC);
+ return(STRINGC); /* XXX delete quotes, -> emit, pbc */
}
-<emit,INITIAL>{HEX} {
- valp->s = str_dup(yytext);
- return(INTC);
- }
-<emit,INITIAL>{BIN} {
- valp->s = str_dup(yytext);
- return(INTC);
- }
-<emit,INITIAL>{STRINGCONSTANT} {
+
+<*>{CHARCONSTANT} {
valp->s = str_dup(yytext); /* XXX delete quotes, -> emit, pbc */
return(STRINGC);
}
-<emit,INITIAL>{CHARCONSTANT} {
- valp->s = str_dup(yytext);
- return(STRINGC);
- }
-<emit,INITIAL>\$I[0-9]+ {
- valp->s = str_dup(yytext);
- return(IREG);
- }
+<*>\$I[0-9]+ DUP_AND_RET(valp, IREG);
+<*>\$N[0-9]+ DUP_AND_RET(valp, NREG);
+<*>\$S[0-9]+ DUP_AND_RET(valp, SREG);
+<*>\$P[0-9]+ DUP_AND_RET(valp, PREG);
-<emit,INITIAL>\$N[0-9]+ {
- valp->s = str_dup(yytext);
- return(NREG);
- }
-
-<emit,INITIAL>\$S[0-9]+ {
- valp->s = str_dup(yytext);
- return(SREG);
- }
+<emit,INITIAL>{WS}+ /* skip */;
-<emit,INITIAL>\$P[0-9]+ {
- valp->s = str_dup(yytext);
- return(PREG);
- }
-
-<emit,INITIAL>[\t\f\r ]+ ;
<emit,INITIAL>. {
return yytext[0];
}
@@ -213,7 +289,55 @@
return 0;
}
-<<EOF>> yyterminate();
+<INITIAL><<EOF>> yyterminate();
+
+<macro>".endm" DUP_AND_RET(valp, ENDM);
+
+<macro>{WS}*{EOL} {
+ line++;
+ DUP_AND_RET(valp, '\n');
+ }
+
+<macro>"$"{ID}":" return LABEL;
+<macro>".local"{WS}+ {
+ char *label;
+ char *name = macros[num_macros].name;
+
+ if (yylex(valp, interp) != LABEL) yyerror("LABEL expected");
+
+ if (valp) {
+ YYCHOP();
+
+ label = mem_sys_allocate(strlen(name) + yyleng + 15);
+ sprintf(label, "local__%s__%s__$:", name, yytext+1);
+
+ valp->s = label;
+ }
+
+ return LABEL;
+ }
+
+<macro>".$"{ID} {
+ char *label;
+ char *name = macros[num_macros].name;
+
+ if (valp) {
+ label = mem_sys_allocate(strlen(name) + yyleng + 15);
+ sprintf(label, "local__%s__%s__$", name, yytext+2);
+
+ valp->s = label;
+ }
+
+ return IDENTIFIER;
+ }
+
+<macro>^{WS}+ /* skip leading ws */;
+<macro>{WS}+ DUP_AND_RET(valp, ' ');
+<macro>{ID} DUP_AND_RET(valp, IDENTIFIER);
+<macro>{DOT}{ID} DUP_AND_RET(valp, MACRO);
+<macro>. DUP_AND_RET(valp, yytext[0]);
+<macro><<EOF>> yyterminate();
+
%%
#ifdef yywrap
@@ -225,6 +349,280 @@
yywrap returns 0 if scanning is to continue
*/
yy_delete_buffer(YY_CURRENT_BUFFER);
+
+ /* pop old frames */
+ if (frames) {
+ struct macro_frame_t *tmp;
+ tmp = frames;
+ frames = frames->next;
+ destroy_frame(tmp);
+ return 0;
+ }
+
+ return 1;
+}
+
+static struct macro_frame_t *
+new_frame (void)
+{
+ static int label = 0;
+ struct macro_frame_t *tmp;
+
+ tmp = mem_sys_allocate_zeroed(sizeof(struct macro_frame_t));
+ tmp->label = ++label;
+
+ return tmp;
+}
+
+static void
+scan_string (struct macro_frame_t *frame, const char *expansion)
+{
+ frame->buffer = YY_CURRENT_BUFFER;
+ frame->next = frames;
+ frames = frame;
+
+ yy_scan_string(expansion);
+}
+
+static void
+destroy_frame (struct macro_frame_t *frame)
+{
+ YY_BUFFER_STATE buffer;
+ int i;
+
+ buffer = frame->buffer;
+
+ for (i = 0; i < frame->expansion.num_param; i++) {
+ free(frame->expansion.name[i]);
+ }
+
+ mem_sys_free(frame);
+
+ yy_switch_to_buffer(buffer);
+}
+
+static int
+yylex_skip (YYSTYPE *valp, void *interp, const char *skip)
+{
+ int c;
+ const char *p;
+
+ do {
+ c = yylex(NULL, interp);
+ p = skip;
+ while (*p && c != *p) p++;
+ } while (*p != '\0');
+
+ DUP_AND_RET(valp, c);
+}
+
+static int
+read_params (YYSTYPE *valp, void *interp, struct params_t *params,
+ int need_id)
+{
+ int c;
+ YYSTYPE val;
+ char *current = strdup("");
+ int len = 0;
+
+ params->num_param = 0;
+ c = yylex_skip(&val, interp, " \n");
+
+ while(c != ')') {
+ if (c == 0) yyerror ("Unexpected end of file");
+ else if (c == ',') {
+ params->name[params->num_param++] = current;
+ current = strdup("");
+ len = 0;
+ c = yylex_skip(&val, interp, " \n");
+ }
+ else if (need_id && (*current || c != IDENTIFIER) && c != ' ') {
+ yyerror("macro parameter error");
+ }
+ else {
+ if (!need_id || c != ' ') {
+ len += strlen(val.s);
+ current = realloc(current, len + 1);
+ strcat(current,val.s);
+ }
+ free(val.s);
+ c = yylex(&val,interp);
+ }
+ }
+ params->name[params->num_param++] = current;
+
+ if (valp) *valp = val;
+ else free(val.s);
+
+ return c;
+}
+
+static int
+read_macro (YYSTYPE *valp, void *interp)
+{
+ int c;
+ struct macro_t *m = macros + num_macros;
+ int start_cond;
+
+ temp_buffer[0]='\0';
+
+ start_cond = YY_START;
+ BEGIN(macro);
+
+ c = yylex(NULL, interp);
+ if (c != ' ') yyerror("macro error");
+
+ c = yylex(valp, interp);
+ if (c != IDENTIFIER) yyerror("macro error");
+
+ m->name = valp->s;
+
+ /* white space is allowed between macro and opening paren) */
+ c = yylex_skip(valp, interp, " ");
+
+ if (c == '(') {
+ free(valp->s);
+
+ c = read_params(NULL, interp, &m->params, 1);
+ if (c != ')') yyerror("macro parameter: \")\" expected");
+
+ c = yylex(valp, interp);
+ }
+
+ while (c != ENDM) {
+ if (c == 0) yyerror("file ended before macro complete");
+
+ strcat(temp_buffer, valp->s);
+ free(valp->s);
+
+ c = yylex(valp, interp);
+ }
+ free(valp->s);
+
+ BEGIN(start_cond);
+
+ macros[num_macros].expansion = str_dup(temp_buffer);
+
+ num_macros++;
+ return MACRO;
+}
+
+static char *
+find_macro_param (const char *name)
+{
+ struct macro_frame_t *f;
+ int i;
+
+ for (f = frames; f; f = f->next) {
+ if (f->params) {
+ for (i = 0; i < f->params->num_param; i++) {
+ if (strcmp(f->params->name[i], name) == 0) {
+ return f->expansion.name[i];
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static struct macro_t *
+find_macro (const char* name)
+{
+ int i;
+
+ for (i = 0; i < num_macros; i++) {
+ if (strcmp(name, macros[i].name) == 0) return macros + i;
+ }
+
+ return NULL;
+}
+
+static int
+expand_macro (YYSTYPE *valp, void *interp, const char *name)
+{
+ int c;
+ struct macro_frame_t *frame;
+ struct macro_t *m;
+ const char *expansion;
+ int start_cond;
+
+ frame = new_frame();
+
+ expansion = find_macro_param(name);
+ if (expansion) {
+ scan_string(frame, expansion);
+ return 1;
+ }
+
+ m = find_macro(name);
+ frame->params = &m->params;
+ if (m) {
+ /* whitespace can be savely ignored */
+ do {
+ c = input();
+ } while (c == ' ' || c == '\t');
+
+ if (c != '(') {
+ unput(c);
+ if (m->params.num_param != 0) yyerror ("Missing macro parameter");
+ scan_string(frame, m->expansion);
return 1;
}
+ start_cond = YY_START;
+ BEGIN(macro);
+
+ read_params (NULL, interp, &frame->expansion, 0);
+
+ BEGIN(start_cond);
+
+ if (frame->expansion.num_param == 0 && m->params.num_param == 1) {
+ frame->expansion.name[0] = str_dup("");
+ frame->expansion.num_param = 1;
+ }
+
+ if (frame->expansion.num_param != m->params.num_param) {
+ yyerror ("Wrong number of macro arguments");
+ }
+
+ scan_string(frame, m->expansion);
+ return 1;
+ }
+
+ return 0;
+}
+
+static void
+include_file (const char *file_name)
+{
+ struct macro_frame_t *frame;
+ FILE *file;
+
+ frame = new_frame();
+
+ file = fopen(file_name, "r");
+ if (!file) yyerror ("file not found");
+
+ scan_file (frame, file);
+}
+
+static void
+scan_file (struct macro_frame_t *frame, FILE *file)
+{
+ frame->buffer = YY_CURRENT_BUFFER;
+ frame->next = frames;
+ frames = frame;
+
+ yy_switch_to_buffer(yy_create_buffer(file, YY_BUF_SIZE));
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: bsd
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vim: expandtab shiftwidth=4:
+ */
1.45 +12 -7 parrot/languages/imcc/imcc.y
Index: imcc.y
===================================================================
RCS file: /cvs/public/parrot/languages/imcc/imcc.y,v
retrieving revision 1.44
retrieving revision 1.45
diff -u -w -r1.44 -r1.45
--- imcc.y 11 Feb 2003 11:34:36 -0000 1.44
+++ imcc.y 17 Feb 2003 13:56:36 -0000 1.45
@@ -169,6 +169,7 @@
return 0;
}
+#if 0
/* return the index of a PMC class */
static int
get_pmc_num(struct Parrot_Interp *interpreter, char *pmc_type)
@@ -191,6 +192,7 @@
r = mk_const(str_dup(buf), 'I');
return r;
}
+#endif
static Instruction *
multi_keyed(struct Parrot_Interp *interpreter,char *name,
@@ -408,14 +410,14 @@
%token <t> CALL GOTO ARG IF UNLESS NEW END SAVEALL RESTOREALL
%token <t> SUB NAMESPACE ENDNAMESPACE CLASS ENDCLASS SYM LOCAL CONST PARAM
-%token <t> INC DEC
+%token <t> CONSTANT INC DEC
%token <t> SHIFT_LEFT SHIFT_RIGHT INTV FLOATV STRINGV DEFINED LOG_XOR
%token <t> RELOP_EQ RELOP_NE RELOP_GT RELOP_GTE RELOP_LT RELOP_LTE
%token <t> GLOBAL ADDR CLONE RESULT RETURN POW SHIFT_RIGHT_U LOG_AND LOG_OR
%token <t> COMMA ESUB
%token <s> LABEL
%token <t> EMIT EOM
-%token <s> IREG NREG SREG PREG IDENTIFIER STRINGC INTC FLOATC REG MACRO
+%token <s> IREG NREG SREG PREG IDENTIFIER STRINGC INTC FLOATC REG MACRO ENDM
%token <s> PARROT_OP
%type <t> type
%type <i> program sub sub_start emit
@@ -425,7 +427,7 @@
%type <sr> target reg const var rc string
%type <sr> key keylist _keylist newtype
%type <sr> vars _vars var_or_i _var_or_i
-%type <i> pasmcode pasmline pasm_inst
+%type <i> pasmcode pasmline pasm_inst constant_def
%type <sr> pasm_args lhs
%token <sr> VAR
@@ -448,16 +450,21 @@
;
pasmline: labels pasm_inst '\n' { $$ = 0; }
+ | MACRO '\n' { $$ = 0; }
+ | constant_def
;
+
pasm_inst: {clear_state();}
PARROT_OP pasm_args { $$ = iANY(interp, $2,0,regs,1); free($2); }
| /* none */ { $$ = 0;}
-
;
pasm_args:
vars
;
+constant_def: CONSTANT IDENTIFIER const { /* printf ("%s\n", $1); */ }
+ ;
+
emit:
EMIT { open_comp_unit(); }
pasmcode
@@ -586,8 +593,7 @@
;
newtype:
- MACRO { $$ = macro(interp, $1); free($1); }
- | const
+ const
;
if_statement:
@@ -638,7 +644,6 @@
IDENTIFIER { $$ = mk_address($1, U_add_once); }
| PARROT_OP { $$ = mk_address($1, U_add_once); }
| var
- | MACRO { $$ = macro(interp, $1); free($1); }
;
var: VAR
1.25 +6 -1 parrot/languages/imcc/instructions.c
Index: instructions.c
===================================================================
RCS file: /cvs/public/parrot/languages/imcc/instructions.c,v
retrieving revision 1.24
retrieving revision 1.25
diff -u -w -r1.24 -r1.25
--- instructions.c 10 Feb 2003 10:04:15 -0000 1.24
+++ instructions.c 17 Feb 2003 13:56:36 -0000 1.25
@@ -212,7 +212,10 @@
next = ins->next;
prev = ins->prev;
+ if (prev)
prev->next = next;
+ else
+ instructions = next;
if (next)
next->prev = prev;
if (needs_freeing)
@@ -244,6 +247,8 @@
Instruction *prev = ins->prev;
if (prev)
prev->next = tmp;
+ else
+ instructions = tmp;
tmp->prev = prev;
tmp->next = ins->next;
if (ins->next)
1.16 +1 -2 parrot/languages/imcc/main.c
Index: main.c
===================================================================
RCS file: /cvs/public/parrot/languages/imcc/main.c,v
retrieving revision 1.15
retrieving revision 1.16
diff -u -w -r1.15 -r1.16
--- main.c 10 Feb 2003 10:04:15 -0000 1.15
+++ main.c 17 Feb 2003 13:56:36 -0000 1.16
@@ -216,7 +216,6 @@
}
ext = strrchr(sourcefile, '.');
if (ext && strcmp (ext, ".pasm") == 0) {
- if (*optimizer_opt == '0')
pasm_file = 1;
if (output)
write_pbc = 1;
1.15 +522 -26 parrot/languages/imcc/optimizer.c
Index: optimizer.c
===================================================================
RCS file: /cvs/public/parrot/languages/imcc/optimizer.c,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -w -r1.14 -r1.15
--- optimizer.c 10 Feb 2003 10:04:15 -0000 1.14
+++ optimizer.c 17 Feb 2003 13:56:36 -0000 1.15
@@ -12,35 +12,30 @@
/*
- * current default optimization is -O1 (s. imcc.y)
- *
- *
* pre_optimizer
* -------------
*
* runs before CFG is built
*
* if_branch ... converts if/branch/label like so:
- *
- * if x, L1 unless x, L2
- * branch L2 ---
- * L1: L2:
- * ...
- * L2:
- *
* unused_label ... deletes them (as L1 above)
- *
- * stages currently not needed by ocde from perl6:
- * jump optimization (jumps to jumps ...)
- *
+ * branch_branch ... jump optimization (jumps to jumps ...)
+ * strength_reduce ... rewrites e.g add Ix, Ix, y => add Ix, y
+ * subst_constants ... rewrite e.g. add_i_ic_ic
*
* optimizer
* ---------
*
- * TODO e.g. constant_propagation
+ * runs with CFG and life info
*
+ * used_once ... deletes assignments, when LHS is unused
+ * dead_code_remove ... deletes e.g. blocks that are not entered from
+ * somewhere or ins after a branch, which aren't
+ * reachable
+ * loop_optimization ... pull invariants out of loops
+ * TODO e.g. constant_propagation
*
- * post_optimizer
+ * TODO post_optimizer
* ---------------
*
* runs after register alloocation
@@ -53,6 +48,11 @@
static void branch_branch(void);
static void unused_label(void);
static void strength_reduce(struct Parrot_Interp *interp);
+static void subst_constants_mix(struct Parrot_Interp *interp);
+static void subst_constants_umix(struct Parrot_Interp *interp);
+static void subst_constants(struct Parrot_Interp *interp);
+static void subst_constants_c(struct Parrot_Interp *interp);
+static void subst_constants_if(struct Parrot_Interp *interp);
static int used_once(void);
static int loop_optimization(struct Parrot_Interp *);
@@ -60,11 +60,16 @@
void pre_optimize(struct Parrot_Interp *interp) {
if (*optimizer_opt != '0') { /* XXX */
+ subst_constants_mix(interp);
+ subst_constants_umix(interp);
+ subst_constants(interp);
+ subst_constants_c(interp);
+ subst_constants_if(interp);
+ strength_reduce(interp);
if_branch(interp);
branch_branch();
/* XXX cfg / loop detection breaks e.g. in t/compiler/5_3 */
unused_label();
- strength_reduce(interp);
}
}
@@ -123,18 +128,19 @@
if (!last->next)
return;
for (ins = last->next; ins; ) {
- if ((last->type & ITBRANCH) && /* if ...Lx */
- (ins->type & IF_goto) && /* branch Ly*/
+ if ((last->type & ITBRANCH) && /* if ...L1 */
+ (ins->type & IF_goto) && /* branch L2*/
(reg = get_branch_regno(last)) >= 0) {
SymReg * br_dest = last->r[reg];
if (ins->next &&
- (ins->next->type & ITLABEL) && /* Lx */
+ (ins->next->type & ITLABEL) && /* L1 */
ins->next->r[0] == br_dest) {
const char * neg_op;
SymReg * go = get_branch_reg(ins);
int args;
- debug(DEBUG_OPT1,"if_branch %s ... %s\n", last->op, br_dest->name);
+ debug(DEBUG_OPT1,"if_branch %s ... %s\n",
+ last->op, br_dest->name);
/* find the negated op (e.g if->unless, ne->eq ... */
if ((neg_op = get_neg_op(last->op, &args)) != 0) {
Instruction * tmp;
@@ -232,28 +238,518 @@
}
}
+/* these are run after constant simplification, so it is
+ * guaranteed, that one operand is non constant, if opsize == 4
+ */
static void strength_reduce(struct Parrot_Interp *interp)
{
Instruction *ins, *tmp;
const char *ops[] = { "add", "sub", "mul", "div" };
- int i;
+ int i, found;
+ SymReg *r;
for (ins = instructions; ins; ins = ins->next) {
/*
+ * add Ix, Ix, Iy => add Ix, Iy
* sub Ix, Ix, Iy => sub Ix, Iy
+ * ...
*/
- for (i = 0; i < 4; i++)
- if (!strcmp(ins->op, ops[i]) && ins->opsize == 4 &&
- ins->r[0] == ins->r[1]) {
+ found = 0;
+ for (i = 0; i < 4; i++) {
+ if (ins->opsize == 4 &&
+ ins->r[0] == ins->r[1] &&
+ !strcmp(ins->op, ops[i])) {
debug(DEBUG_OPT1, "opt1 %s => ", ins_string(ins));
ins->r[1] = ins->r[2];
tmp = INS(interp, ins->op, "", ins->r, 2, 0, 0);
debug(DEBUG_OPT1, "%s\n", ins_string(tmp));
subst_ins(ins, tmp, 1);
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ continue;
+ /*
+ * mul Ix, Iy, 0 => set Ix, 0
+ * mul Ix, 0, Iy => set Ix, 0
+ * mul Ix, 0 => set Ix, 0
+ */
+ if ( ( ( ins->opsize >= 3 &&
+ ins->r[1]->type == VTCONST &&
+ atof(ins->r[1]->name) == 0.0) ||
+ (ins->opsize == 4 &&
+ ins->r[2]->type == VTCONST &&
+ atof(ins->r[2]->name) == 0.0)) &&
+ !strcmp(ins->op, "mul")) {
+ debug(DEBUG_OPT1, "opt1 %s => ", ins_string(ins));
+ r = mk_const(ins->r[0]->set == 'I' ? str_dup("0") :
+ str_dup("0.000000"),
+ ins->r[0]->set);
+ --ins->r[1]->use_count;
+ if (ins->opsize == 4)
+ --ins->r[2]->use_count;
+ ins->r[1] = r;
+ tmp = INS(interp, "set", "", ins->r, 2, 0, 0);
+ debug(DEBUG_OPT1, "%s\n", ins_string(tmp));
+ subst_ins(ins, tmp, 1);
+ continue;
+ }
+ /*
+ * mul Ix, Iy, 1 => set Ix, Iy
+ * mul Ix, 1, Iy => set Ix, Iy
+ * mul Ix, 1 => delete
+ */
+ if ( ( ( ins->opsize >= 3 &&
+ ins->r[1]->type == VTCONST &&
+ atof(ins->r[1]->name) == 1.0) ||
+ (ins->opsize == 4 &&
+ ins->r[2]->type == VTCONST &&
+ atof(ins->r[2]->name) == 1.0)) &&
+ !strcmp(ins->op, "mul")) {
+set_it:
+ debug(DEBUG_OPT1, "opt1 %s => ", ins_string(ins));
+ if (ins->opsize == 3) {
+ /* mul Ix, 1 */
+ delete_ins(ins, 1);
+ debug(DEBUG_OPT1, "deleted\n");
+ continue;
+ }
+ if (ins->r[1]->type == VTCONST) {
+ --ins->r[1]->use_count;
+ ins->r[1] = ins->r[2];
+ }
+ else {
+ --ins->r[2]->use_count;
+ }
+ tmp = INS(interp, "set", "", ins->r, 2, 0, 0);
+ debug(DEBUG_OPT1, "%s\n", ins_string(tmp));
+ subst_ins(ins, tmp, 1);
+ continue;
+ }
+ /*
+ * div Ix, Iy, 1 => set Ix, Iy
+ * div Ix, 1 => delete
+ */
+ if ( ( ( ins->opsize == 3 &&
+ ins->r[1]->type == VTCONST &&
+ atof(ins->r[1]->name) == 1.0) ||
+ (ins->opsize == 4 &&
+ ins->r[2]->type == VTCONST &&
+ atof(ins->r[2]->name) == 1.0)) &&
+ !strcmp(ins->op, "div")) {
+ goto set_it;
+ }
+ }
+}
+
+/*
+ * rewrite e.g. add_n_nc_ic => add_n_nc_nc
+ */
+static void
+subst_constants_mix(struct Parrot_Interp *interp)
+{
+ Instruction *ins, *tmp;
+ const char *ops[] = {
+ "add", "sub", "mul", "div", "pow", "atan"
+ };
+ size_t i;
+ char b[128];
+ SymReg *r;
+
+ for (ins = instructions; ins; ins = ins->next) {
+ for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) {
+ /* TODO compare ins->opnum with a list of instructions
+ * containing add_n_nc_ic, ...
+ */
+ if (!strcmp(ins->op, ops[i]) && ins->opsize == 4 &&
+ ins->r[1]->type == VTCONST &&
+ ins->r[1]->set == 'N' &&
+ ins->r[2]->type == VTCONST &&
+ ins->r[2]->set == 'I') {
+ debug(DEBUG_OPT1, "opt1 %s => ", ins_string(ins));
+ sprintf(b, FLOATVAL_FMT,
+ (FLOATVAL)atof(ins->r[2]->name));
+ r = mk_const(str_dup(b), 'N');
+ --ins->r[2]->use_count;
+ ins->r[2] = r;
+ tmp = INS(interp, ins->op, "", ins->r, 3, 0, 0);
+ debug(DEBUG_OPT1, "%s\n", ins_string(tmp));
+ subst_ins(ins, tmp, 1);
+ }
+ }
+ }
+}
+
+/*
+ * rewrite e.g. add_n_ic => add_n_nc
+ */
+static void
+subst_constants_umix(struct Parrot_Interp *interp)
+{
+ Instruction *ins, *tmp;
+ const char *ops[] = {
+ "add", "div", "mul", "sub", "set"
+ };
+ size_t i;
+ char b[128];
+ SymReg *r;
+
+ for (ins = instructions; ins; ins = ins->next) {
+ for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) {
+ /* TODO compare ins->opnum with a list of instructions
+ * containing add_n_ic, ...
+ */
+ if (ins->opsize == 3 &&
+ ins->r[0]->set == 'N' &&
+ ins->r[1]->type == VTCONST &&
+ ins->r[1]->set == 'I' &&
+ !strcmp(ins->op, ops[i])) {
+ debug(DEBUG_OPT1, "opt1 %s => ", ins_string(ins));
+ sprintf(b, FLOATVAL_FMT,
+ (FLOATVAL)atof(ins->r[1]->name));
+ r = mk_const(str_dup(b), 'N');
+ --ins->r[1]->use_count;
+ ins->r[1] = r;
+ tmp = INS(interp, ins->op, "", ins->r, 2, 0, 0);
+ debug(DEBUG_OPT1, "%s\n", ins_string(tmp));
+ subst_ins(ins, tmp, 1);
+ }
+ }
+ }
+}
+
+static void
+eval_ins(struct Parrot_Interp *interpreter, Instruction *ins, size_t ops)
+{
+ opcode_t eval[4];
+ char op[20];
+ int opnum;
+ size_t i;
+
+ /*
+ * find instruction e.g. add_i_ic_ic => add_i_i_i
+ */
+ if (ops == 4)
+ sprintf(op, "%s_%c_%c_%c", ins->op, tolower(ins->r[0]->set),
+ tolower(ins->r[1]->set), tolower(ins->r[2]->set));
+ else
+ sprintf(op, "%s_%c_%c", ins->op, tolower(ins->r[0]->set),
+ tolower(ins->r[1]->set));
+ opnum = interpreter->op_lib->op_code(op, 1);
+ if (opnum < 0)
+ fatal(1, "eval_ins", "op '%s' not found\n", op);
+ /* now fill registers */
+ for (i = 0; i < ops; i++) {
+ switch (interpreter->op_info_table[opnum].types[i]) {
+ case PARROT_ARG_OP:
+ eval[i] = opnum;
+ break;
+ case PARROT_ARG_I:
+ case PARROT_ARG_N:
+ eval[i] = i - 1; /* result in I0/N0 */
+ if (i > 1) { /* fill source regs */
+ switch (ins->r[i-1]->set) {
+ case 'I':
+ interpreter->ctx.int_reg.registers[i-1] =
+ (INTVAL)atoi(ins->r[i-1]->name);
+ break;
+ case 'N':
+ interpreter->ctx.num_reg.registers[i-1] =
+ (FLOATVAL)atof(ins->r[i-1]->name);
+ break;
+ }
+ }
+ break;
+ default:
+ fatal(1, "eval_ins", "invalid arg #%d for op '%s' not found\n",
+ i, op);
+ }
+ }
+
+ /* eval the opcode */
+ (interpreter->op_func_table[opnum]) (eval, interpreter);
+}
+
+/*
+ * rewrite e.g. add_n_nc_nc => set_n_nc
+ * or abs_i_ic => set_i_ic
+ */
+static void
+subst_constants(struct Parrot_Interp *interp)
+{
+ Instruction *ins, *tmp;
+ const char *ops[] = {
+ "add", "sub", "mul", "div", "cmod", "mod", "pow", "atan"
+ "shr", "srl", "lsr",
+ "band", "bor", "bxor",
+ "and", "or", "xor"
+ };
+ const char *ops2[] = {
+ "abs", "neg", "acos", "asec", "asin",
+ "atan", "cos", "cosh", "exp", "ln", "log10", "log2", "sec",
+ "sech", "sin", "sinh", "tan", "tanh", "fact"
+ };
+ size_t i;
+ char b[128];
+ SymReg *r;
+ struct Parrot_Context *ctx;
+ int found;
+
+ /* save interpreter ctx */
+ ctx = mem_sys_allocate(sizeof(struct Parrot_Context));
+ mem_sys_memcopy(ctx, &interp->ctx, sizeof(struct Parrot_Context));
+
+ for (ins = instructions; ins; ins = ins->next) {
+ found = 0;
+ for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) {
+ /* TODO compare ins->opnum with a list of instructions
+ * containing add_i_ic_ic, add_n_nc_nc ...
+ */
+ if (ins->opsize == 4 &&
+ ins->r[1]->type == VTCONST &&
+ ins->r[2]->type == VTCONST &&
+ !strcmp(ins->op, ops[i])) {
+ found = 1;
+ break;
+ }
+ }
+ for (i = 0; !found && i < sizeof(ops2)/sizeof(ops2[0]); i++) {
+ if (ins->opsize == 3 &&
+ ins->r[1]->type == VTCONST &&
+ !strcmp(ins->op, ops2[i])) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ break;
+ debug(DEBUG_OPT1, "opt1 %s => ", ins_string(ins));
+ /* we construct a parrot instruction
+ * here and let parrot do the calculation in a
+ * separate context and make a constant
+ * from the result
+ */
+ eval_ins(interp, ins, ins->opsize);
+ /* result is in I0/N0 */
+ switch (ins->r[0]->set) {
+ case 'I':
+ sprintf(b, INTVAL_FMT, interp->ctx.int_reg.registers[0]);
+ break;
+ case 'N': /* FIXME precision/exponent */
+ sprintf(b, FLOATVAL_FMT, interp->ctx.num_reg.registers[0]);
+ break;
+ }
+ r = mk_const(str_dup(b), ins->r[0]->set);
+ --ins->r[1]->use_count;
+ if (ins->opsize == 4)
+ --ins->r[2]->use_count;
+ ins->r[1] = r;
+ tmp = INS(interp, "set", "", ins->r, 2, 0, 0);
+ debug(DEBUG_OPT1, "%s\n", ins_string(tmp));
+ subst_ins(ins, tmp, 1);
+ }
+ mem_sys_memcopy(&interp->ctx, ctx, sizeof(struct Parrot_Context));
+ mem_sys_free(ctx);
+}
+
+/*
+ * rewrite e.g. eq_ic_ic_ic => branch_ic/nothing
+ */
+static void
+subst_constants_c(struct Parrot_Interp *interp)
+{
+ Instruction *ins, *tmp;
+ const char *ops[] = { "eq", "ne", "gt", "ge", "lt", "le" };
+ size_t i;
+ int res;
+
+ for (ins = instructions; ins; ins = ins->next) {
+ for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) {
+ /* TODO s. above */
+ if (ins->opsize == 4 &&
+ ins->r[0]->type == VTCONST &&
+ ins->r[1]->type == VTCONST &&
+ !strcmp(ins->op, ops[i])) {
+ debug(DEBUG_OPT1, "opt1 %s => ", ins_string(ins));
+ switch (i) {
+ case 0: /* eq */
+ switch (ins->r[0]->set) {
+ case 'I':
+ res = (atoi(ins->r[0]->name) ==
+ atoi(ins->r[1]->name));
+do_res:
+ --ins->r[0]->use_count;
+ --ins->r[1]->use_count;
+ if (res) {
+ ins->r[0] = ins->r[2];
+ tmp = INS(interp, "branch", "", ins->r,
+ 1, 0, 0);
+ debug(DEBUG_OPT1, "%s\n", ins_string(tmp));
+ subst_ins(ins, tmp, 1);
+ }
+ else {
+ debug(DEBUG_OPT1, "deleted\n");
+ delete_ins(ins, 1);
+ }
+ break;
+ case 'N':
+ res = (atof(ins->r[0]->name) ==
+ atof(ins->r[1]->name));
+ goto do_res;
+ case 'S':
+ res = !strcmp(ins->r[0]->name,
+ ins->r[1]->name);
+ goto do_res;
+ }
+ break;
+ case 1: /* ne */
+ switch (ins->r[0]->set) {
+ case 'I':
+ res = (atoi(ins->r[0]->name) !=
+ atoi(ins->r[1]->name));
+ goto do_res;
+ case 'N':
+ res = (atof(ins->r[0]->name) !=
+ atof(ins->r[1]->name));
+ goto do_res;
+ case 'S':
+ res = strcmp(ins->r[0]->name,
+ ins->r[1]->name);
+ goto do_res;
+ }
+ break;
+ case 2: /* gt */
+ switch (ins->r[0]->set) {
+ case 'I':
+ res = (atoi(ins->r[0]->name) >
+ atoi(ins->r[1]->name));
+ goto do_res;
+ case 'N':
+ res = (atof(ins->r[0]->name) >
+ atof(ins->r[1]->name));
+ goto do_res;
+ case 'S':
+ res = strcmp(ins->r[0]->name,
+ ins->r[1]->name) > 0;
+ goto do_res;
+ }
+ break;
+ case 3: /* ge */
+ switch (ins->r[0]->set) {
+ case 'I':
+ res = (atoi(ins->r[0]->name) >=
+ atoi(ins->r[1]->name));
+ goto do_res;
+ case 'N':
+ res = (atof(ins->r[0]->name) >=
+ atof(ins->r[1]->name));
+ goto do_res;
+ case 'S':
+ res = strcmp(ins->r[0]->name,
+ ins->r[1]->name) >= 0;
+ goto do_res;
+ }
+ break;
+ case 4: /* lt */
+ switch (ins->r[0]->set) {
+ case 'I':
+ res = (atoi(ins->r[0]->name) <
+ atoi(ins->r[1]->name));
+ goto do_res;
+ case 'N':
+ res = (atof(ins->r[0]->name) <
+ atof(ins->r[1]->name));
+ goto do_res;
+ case 'S':
+ res = strcmp(ins->r[0]->name,
+ ins->r[1]->name) < 0;
+ goto do_res;
+ }
+ break;
+ case 5: /* le */
+ switch (ins->r[0]->set) {
+ case 'I':
+ res = (atoi(ins->r[0]->name) <=
+ atoi(ins->r[1]->name));
+ goto do_res;
+ case 'N':
+ res = (atof(ins->r[0]->name) <=
+ atof(ins->r[1]->name));
+ goto do_res;
+ case 'S':
+ res = strcmp(ins->r[0]->name,
+ ins->r[1]->name) <= 0;
+ goto do_res;
+ }
+ break;
+ }
+ }
+ }
}
}
+/*
+ * rewrite e.g. if_ic_ic => branch_ic/nothing
+ */
+static void
+subst_constants_if(struct Parrot_Interp *interp)
+{
+ Instruction *ins, *tmp;
+ const char *ops[] = { "if", "unless" };
+ size_t i;
+ int res;
+ char *s;
+
+ for (ins = instructions; ins; ins = ins->next) {
+ for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) {
+ /* TODO s. above */
+ if (ins->opsize == 3 &&
+ ins->r[0]->type == VTCONST &&
+ !strcmp(ins->op, ops[i])) {
+ debug(DEBUG_OPT1, "opt1 %s => ", ins_string(ins));
+ switch (i) {
+ case 0: /* if */
+ case 1: /* unless */
+ switch (ins->r[0]->set) {
+ case 'I':
+ res = atoi(ins->r[0]->name);
+do_res:
+ --ins->r[0]->use_count;
+ if (i)
+ res = !res;
+ if (res) {
+ ins->r[0] = ins->r[1];
+ tmp = INS(interp, "branch", "", ins->r,
+ 1, 0, 0);
+ debug(DEBUG_OPT1, "%s\n", ins_string(tmp));
+ subst_ins(ins, tmp, 1);
+ }
+ else {
+ debug(DEBUG_OPT1, "deleted\n");
+ delete_ins(ins, 1);
}
+ break;
+ case 'N':
+ res = atof(ins->r[1]->name) != 0.0;
+ goto do_res;
+ case 'S':
+ break;
+ /* TODO strings have quote marks around them,
+ * strip these in lexer
+ */
+ s = ins->r[0]->name;
+ res = *s && (*s != '0' || s[1]);
+ goto do_res;
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+
+
/* optimizations with CFG built */
int dead_code_remove(void)
{
1.9 +5 -2 parrot/languages/imcc/parser_util.c
Index: parser_util.c
===================================================================
RCS file: /cvs/public/parrot/languages/imcc/parser_util.c,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -w -r1.8 -r1.9
--- parser_util.c 31 Jan 2003 10:54:08 -0000 1.8
+++ parser_util.c 17 Feb 2003 13:56:36 -0000 1.9
@@ -29,10 +29,13 @@
iNEW(struct Parrot_Interp *interpreter, SymReg * r0, char * type, int emit)
{
char fmt[256];
- SymReg *pmc = macro(interpreter, type);
+ SymReg *pmc;
+ int pmc_num = get_pmc_num(interpreter, type);
+ sprintf(fmt, "%d", pmc_num);
+ pmc = mk_const(str_dup(fmt), 'I');
/* XXX check, if type exists, but aove keyed search
* gives 0 for non existing PMCs */
- sprintf(fmt, "%%s, %d\t # .%s", atoi(pmc->name), type);
+ sprintf(fmt, "%%s, %d\t # .%s", pmc_num, type);
r0->usage = U_NEW;
if (!strcmp(type, "PerlArray") || !strcmp(type, "PerlHash"))
r0->usage |= U_KEYED;
1.26 +4 -1 parrot/languages/imcc/pbc.c
Index: pbc.c
===================================================================
RCS file: /cvs/public/parrot/languages/imcc/pbc.c,v
retrieving revision 1.25
retrieving revision 1.26
diff -u -w -r1.25 -r1.26
--- pbc.c 15 Feb 2003 11:25:33 -0000 1.25
+++ pbc.c 17 Feb 2003 13:56:36 -0000 1.26
@@ -610,6 +610,8 @@
{
if (r->color >= 0)
return;
+ if (r->use_count <= 0)
+ return;
switch (r->set) {
case 'I':
if (r->name[0] == '0' && r->name[1] == 'x')
@@ -633,7 +635,8 @@
break;
}
if (r /*&& r->set != 'I' */)
- debug(DEBUG_PBC_CONST,"const %s\tcolor %d\n", r->name, r->color);
+ debug(DEBUG_PBC_CONST,"const %s\tcolor %d use_count %d\n",
+ r->name, r->color, r->use_count);
}
/* store a constants idx for later reuse */
1.15 +5 -2 parrot/languages/imcc/symreg.c
Index: symreg.c
===================================================================
RCS file: /cvs/public/parrot/languages/imcc/symreg.c,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -w -r1.14 -r1.15
--- symreg.c 7 Feb 2003 14:05:51 -0000 1.14
+++ symreg.c 17 Feb 2003 13:56:36 -0000 1.15
@@ -127,6 +127,7 @@
SymReg * _mk_const(SymReg *hsh[], char * name, char t) {
SymReg * r = _mk_symreg(hsh, name, t);
r->type = VTCONST;
+ r->use_count++;
return r;
}
@@ -150,9 +151,10 @@
r->type == VTADDRESS &&
r->lhs_use_count /* we use this for labes/subs */
) {
- if (uniq == U_add_uniq_label)
+ if (uniq == U_add_uniq_label) {
fataly(1, "mk_address", line,
"Label '%s' already defined\n", name);
+ }
else if (uniq == U_add_uniq_sub)
fataly(1, "mk_address", line,
"Subroutine '%s' already defined\n", name);
@@ -242,6 +244,7 @@
if (!keychain)
fatal(1, "link_keys", "Out of mem\n");
keychain->type = VTCONST;
+ ++keychain->use_count;
key = keychain;
for (i = 0; i < nargs; i++) {
/* if any component is a variable, we need to track it in
1.4 +402 -1 parrot/languages/imcc/t/imcpasm/opt1.t
Index: opt1.t
===================================================================
RCS file: /cvs/public/parrot/languages/imcc/t/imcpasm/opt1.t,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -w -r1.3 -r1.4
--- opt1.t 8 Feb 2003 17:16:21 -0000 1.3
+++ opt1.t 17 Feb 2003 13:56:38 -0000 1.4
@@ -1,6 +1,6 @@
#!perl
use strict;
-use TestCompiler tests => 6;
+use TestCompiler tests => 47;
use Test::More qw(skip);
# these tests are run with -O1 by TestCompiler and show
@@ -98,3 +98,404 @@
end
OUT
+##############################
+output_is(<<'CODE', <<'OUT', "constant add");
+ add I0, 10, 15
+ add N0, 10.0, 15.0
+ end
+CODE
+ set I0, 25
+ set N0, 25.000000
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant sub");
+ sub I0, 10, 15
+ sub N0, 10.0, 15.0
+ end
+CODE
+ set I0, -5
+ set N0, -5.000000
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant mul");
+ mul I0, 10, 15
+ mul N0, 10.0, 15.0
+ end
+CODE
+ set I0, 150
+ set N0, 150.000000
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant div");
+ div I0, 10, 5
+ div N0, 10.0, 5.0
+ end
+CODE
+ set I0, 2
+ set N0, 2.000000
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant cmod");
+ cmod I0, 33, 10
+ cmod N0, 33.0, 10.0
+ end
+CODE
+ set I0, 3
+ set N0, 3.000000
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant mod");
+ mod I0, 33, 10
+ mod N0, 33.0, 10.0
+ end
+CODE
+ set I0, 3
+ set N0, 3.000000
+ end
+OUT
+
+##############################
+SKIP: {
+skip("constant concat N/Y", 1);
+output_is(<<'CODE', <<'OUT', "constant concat");
+ concat S0, "Parrot ", "rocks"
+ end
+CODE
+ set S0, "Parrot rocks"
+ end
+OUT
+}
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant eq taken");
+ eq 10, 10, L1
+ set I0, 5
+L1:end
+CODE
+L1:
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant eq not taken");
+ eq 10, 20, L1
+ set I0, 5
+L1:end
+CODE
+ set I0, 5
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant eq taken");
+ eq 10.0, 10.0, L1
+ set I0, 5
+L1:end
+CODE
+L1:
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant eq not taken");
+ eq 10.0, 20.0, L1
+ set I0, 5
+L1:end
+CODE
+ set I0, 5
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant eq taken");
+ eq "xy", "xy", L1
+ set I0, 5
+L1:end
+CODE
+L1:
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant eq not taken");
+ eq "ab", "ba", L1
+ set I0, 5
+L1:end
+CODE
+ set I0, 5
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant ne taken");
+ ne 10, 20, L1
+ set I0, 5
+L1:end
+CODE
+L1:
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant ne not taken");
+ ne 10, 10, L1
+ set I0, 5
+L1:end
+CODE
+ set I0, 5
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant gt taken");
+ gt "xy", "ap", L1
+ set I0, 5
+L1:end
+CODE
+L1:
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant gt not taken");
+ gt "ab", "ba", L1
+ set I0, 5
+L1:end
+CODE
+ set I0, 5
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant ge taken");
+ ge "xy", "xy", L1
+ set I0, 5
+L1:end
+CODE
+L1:
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant ge not taken");
+ gt "ab", "ba", L1
+ set I0, 5
+L1:end
+CODE
+ set I0, 5
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant lt taken");
+ lt "xx", "xy", L1
+ set I0, 5
+L1:end
+CODE
+L1:
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant lt not taken");
+ lt "ba", "ba", L1
+ set I0, 5
+L1:end
+CODE
+ set I0, 5
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant le taken");
+ le "xy", "xy", L1
+ set I0, 5
+L1:end
+CODE
+L1:
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant le not taken");
+ le "bb", "ba", L1
+ set I0, 5
+L1:end
+CODE
+ set I0, 5
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant if taken");
+ if 10, L1
+ set I0, 5
+L1:end
+CODE
+L1:
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant if not taken");
+ if 0, L1
+ set I0, 5
+L1:end
+CODE
+ set I0, 5
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant unless taken");
+ unless 0, L1
+ set I0, 5
+L1:end
+CODE
+L1:
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant unless not taken");
+ unless 1, L1
+ set I0, 5
+L1:end
+CODE
+ set I0, 5
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant mix add");
+ add N0, 10.0, 15
+ end
+CODE
+ set N0, 25.000000
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant unary abs");
+ abs I0, -10
+ end
+CODE
+ set I0, 10
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "constant set");
+ set N0, 5
+ end
+CODE
+ set N0, 5.000000
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "strength mul I, 0");
+ mul I0, 0
+ end
+CODE
+ set I0, 0
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "strength mul I, I, 0");
+ mul I0, I1, 0
+ end
+CODE
+ set I0, 0
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "strength mul I, 0, I");
+ mul I0, 0, I1
+ end
+CODE
+ set I0, 0
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "strength mul N, 0, N");
+ mul N0, 0.0, N1
+ end
+CODE
+ set N0, 0.000000
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "strength mul I, 1");
+ mul I0, 1
+ end
+CODE
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "strength mul I, I, 1");
+ mul I0, I1, 1
+ end
+CODE
+ set I0, I1
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "strength mul I, 1, I");
+ mul I0, 1, I1
+ end
+CODE
+ set I0, I1
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "strength mul N, 1, N");
+ mul N0, 1.0, N1
+ end
+CODE
+ set N0, N1
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "strength div I, 1");
+ div I0, 1
+ end
+CODE
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "strength div I, I, 1");
+ div I0, I1, 1
+ end
+CODE
+ set I0, I1
+ end
+OUT
+
+##############################
+output_is(<<'CODE', <<'OUT', "strength div N, N, 1");
+ div N0, N1, 1
+ end
+CODE
+ set N0, N1
+ end
+OUT