diff --git a/README.md b/README.md index dba3e59f4..11766955e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Inform 7 -v10.1.0-alpha.1+6S96 'Krypton' (11 August 2021) +v10.1.0-alpha.1+6S97 'Krypton' (12 August 2021) ## About Inform 7 diff --git a/build.txt b/build.txt index 27fb03b94..5a886cc03 100644 --- a/build.txt +++ b/build.txt @@ -1,3 +1,3 @@ Prerelease: alpha.1 -Build Date: 11 August 2021 -Build Number: 6S96 +Build Date: 12 August 2021 +Build Number: 6S97 diff --git a/docs/final-module/2-cal.html b/docs/final-module/2-cal.html index 5bc8ead2a..4e99e8be1 100644 --- a/docs/final-module/2-cal.html +++ b/docs/final-module/2-cal.html @@ -461,6 +461,42 @@ is 20. We instead compile this as } +

§3.

+ +
+int CodeGen::CL::node_is_word_array_ref(inter_tree *I, inter_tree_node *P) {
+    int reffed = FALSE;
+    while (P->W.data[ID_IFLD] == REFERENCE_IST) {
+        P = InterTree::first_child(P);
+        reffed = TRUE;
+    }
+    if (P->W.data[ID_IFLD] == INV_IST) {
+        if (P->W.data[METHOD_INV_IFLD] == INVOKED_PRIMITIVE) {
+            inter_symbol *prim = Inter::Inv::invokee(P);
+            inter_ti bip = Primitives::to_bip(I, prim);
+            if (bip == LOOKUPREF_BIP) return TRUE;
+            if ((bip == LOOKUP_BIP) && (reffed)) return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+int CodeGen::CL::node_is_property_ref(inter_tree *I, inter_tree_node *P) {
+    int reffed = FALSE;
+    while (P->W.data[ID_IFLD] == REFERENCE_IST) {
+        P = InterTree::first_child(P);
+        reffed = TRUE;
+    }
+    if (P->W.data[ID_IFLD] == INV_IST) {
+        if (P->W.data[METHOD_INV_IFLD] == INVOKED_PRIMITIVE) {
+            inter_symbol *prim = Inter::Inv::invokee(P);
+            inter_ti bip = Primitives::to_bip(I, prim);
+            if (bip == PROPERTYVALUE_BIP) return TRUE;
+        }
+    }
+    return FALSE;
+}
+
diff --git a/docs/final-module/2-cg.html b/docs/final-module/2-cg.html index 586ed3ae1..f6b3aab0a 100644 --- a/docs/final-module/2-cg.html +++ b/docs/final-module/2-cg.html @@ -157,7 +157,7 @@ only when assembling other material, and not for the final output. return gen; } - +

§3. At present, at least, a "segment" is nothing more than a wrapper for a text. But we abstract it in case it's ever useful for it to be more.

@@ -174,7 +174,7 @@ But we abstract it in case it's ever useful for it to be more. return seg; } -void CodeGen::create_segments(code_generation *gen, void *data, int codes[]) { +void CodeGen::create_segments(code_generation *gen, void *data, int codes[]) { gen->segment_sequence = NEW_LINKED_LIST(generated_segment); for (int i=0; codes[i] >= 0; i++) { if ((codes[i] >= MAX_CG_SEGMENTS) || @@ -201,7 +201,7 @@ always be done in a way which is then undone, restoring the previous state:

-generated_segment *CodeGen::select(code_generation *gen, int i) {
+generated_segment *CodeGen::select(code_generation *gen, int i) {
     generated_segment *saved = gen->current_segment;
     if ((i < 0) || (i >= MAX_CG_SEGMENTS)) internal_error("out of range");
     if (gen->temporarily_diverted) internal_error("poorly timed selection");
@@ -209,7 +209,7 @@ always be done in a way which is then undone, restoring the previous state:
     return saved;
 }
 
-void CodeGen::deselect(code_generation *gen, generated_segment *saved) {
+void CodeGen::deselect(code_generation *gen, generated_segment *saved) {
     if (gen->temporarily_diverted) internal_error("poorly timed deselection");
     gen->current_segment = saved;
 }
@@ -238,7 +238,7 @@ we also have to direct it to a given text.
 

-text_stream *CodeGen::current(code_generation *gen) {
+text_stream *CodeGen::current(code_generation *gen) {
     if (gen->temporarily_diverted)
         return gen->segments[temporary_I7CGS]->generated_code;
     if (gen->current_segment == NULL) return NULL;
diff --git a/docs/final-module/2-fc.html b/docs/final-module/2-fc.html
index f763d743d..c4d4f1573 100644
--- a/docs/final-module/2-fc.html
+++ b/docs/final-module/2-fc.html
@@ -148,7 +148,7 @@ function togglePopup(material_id) {
     }
 }
 
-void CodeGen::FC::frame(code_generation *gen, inter_tree_node *P) {
+void CodeGen::FC::frame(code_generation *gen, inter_tree_node *P) {
     switch (P->W.data[ID_IFLD]) {
         case SYMBOL_IST: break;
         case CONSTANT_IST: {
diff --git a/docs/final-module/5-cim.html b/docs/final-module/5-cim.html
index dc5b2ea92..45035a300 100644
--- a/docs/final-module/5-cim.html
+++ b/docs/final-module/5-cim.html
@@ -82,10 +82,6 @@ function togglePopup(material_id) {
 #include <stdlib.h>
 #include <stdio.h>
 
-typedef struct i7varargs {
-    i7val args[10];
-} i7varargs;
-
 i7val i7_mgl_self = 0;
 i7val i7_mgl_sp = 0;
 #define i7_mgl_Grammar__Version 2
@@ -94,39 +90,6 @@ function togglePopup(material_id) {
 i7val i7_tmp = 0;
 int i7_seed = 197;
 
-#define i7_cpv_SET 1
-#define i7_cpv_PREDEC 2
-#define i7_cpv_POSTDEC 3
-#define i7_cpv_PREINC 4
-#define i7_cpv_POSTINC 5
-
-#define I7BYTE_3(V) ((V & 0xFF000000) >> 24)
-#define I7BYTE_2(V) ((V & 0x00FF0000) >> 16)
-#define I7BYTE_1(V) ((V & 0x0000FF00) >> 8)
-#define I7BYTE_0(V) (V & 0x000000FF)
-
-i7val i7_lookup(i7byte i7bytes[], i7val offset, i7val ind) {
-    ind = offset + 4*ind;
-    return ((i7val) i7bytes[ind]) + 0x100*((i7val) i7bytes[ind+1]) +
-        0x10000*((i7val) i7bytes[ind+2]) + 0x1000000*((i7val) i7bytes[ind+3]);
-}
-
-i7val write_i7_lookup(i7byte i7bytes[], i7val offset, i7val ind, i7val V, int way) {
-    i7val val = i7_lookup(i7bytes, offset, ind);
-    i7val RV = V;
-    switch (way) {
-        case i7_cpv_PREDEC:  RV = val; V = val-1; break;
-        case i7_cpv_POSTDEC: RV = val-1; V = val-1; break;
-        case i7_cpv_PREINC:  RV = val; V = val+1; break;
-        case i7_cpv_POSTINC: RV = val+1; V = val+1; break;
-    }
-    ind = offset + 4*ind;
-    i7bytes[ind]   = I7BYTE_0(V);
-    i7bytes[ind+1] = I7BYTE_1(V);
-    i7bytes[ind+2] = I7BYTE_2(V);
-    i7bytes[ind+3] = I7BYTE_3(V);
-    return RV;
-}
 
 void glulx_accelfunc(i7val x, i7val y) {
     printf("Unimplemented: glulx_accelfunc.\n");
@@ -568,28 +531,25 @@ function togglePopup(material_id) {
 i7val i7_mgl_sharp_grammar_table = 0;
 
 #define i7_mgl_FLOAT_NAN 0
+
+typedef struct i7varargs {
+    i7val args[10];
+} i7varargs;
 

§2.

-
-void CTarget::begin_memory(code_generation *gen) {
-    generated_segment *saved = CodeGen::select(gen, c_header_matter_I7CGS);
-    text_stream *OUT = CodeGen::current(gen);
-    WRITE("i7byte i7mem[];\n");
-    CodeGen::deselect(gen, saved);
-
-    saved = CodeGen::select(gen, c_mem_I7CGS);
-    OUT = CodeGen::current(gen);
-    WRITE("i7byte i7mem[] = {\n");
-    CodeGen::deselect(gen, saved);
+void CTarget::begin_dictionary_words(code_generation *gen) {
 }
 
-void CTarget::end_memory(code_generation *gen) {
-    generated_segment *saved = CodeGen::select(gen, c_mem_I7CGS);
+void CTarget::end_dictionary_words(code_generation *gen) {
+    generated_segment *saved = CodeGen::select(gen, c_predeclarations_I7CGS);
     text_stream *OUT = CodeGen::current(gen);
-    WRITE("0, 0 };\n");
+    for (int i=0; i<C_GEN_DATA(C_dword_count); i++) {
+        WRITE("#define i7_s_dword_%d %d\n", i, 2*i);
+        WRITE("#define i7_p_dword_%d %d\n", i, 2*i + 1);
+    }
     CodeGen::deselect(gen, saved);
 
     saved = CodeGen::select(gen, c_predeclarations_I7CGS);
@@ -598,20 +558,7 @@ function togglePopup(material_id) {
     CodeGen::deselect(gen, saved);
 }
 
-void CTarget::begin_dictionary_words(code_generation *gen) {
-}
-
-void CTarget::end_dictionary_words(code_generation *gen) {
-    generated_segment *saved = CodeGen::select(gen, c_predeclarations_I7CGS);
-    text_stream *OUT = CodeGen::current(gen);
-    for (int i=0; i<C_GEN_DATA(C_dword_count); i++) {
-        WRITE("#define i7_s_dword_%d %d\n", i, 2*i);
-        WRITE("#define i7_p_dword_%d %d\n", i, 2*i + 1);
-    }
-    CodeGen::deselect(gen, saved);
-}
-
-void CTarget::end_functions(code_generation *gen) {
+void CTarget::end_functions(code_generation *gen) {
     generated_segment *saved = CodeGen::select(gen, c_globals_array_I7CGS);
     text_stream *OUT = CodeGen::current(gen);
     WRITE("#ifdef i7_defined_i7_mgl_I7S_Comp\n");
@@ -631,7 +578,7 @@ function togglePopup(material_id) {
     CodeGen::deselect(gen, saved);
 }
 
-void CTarget::fix_locals(code_generation *gen) {
+void CTarget::fix_locals(code_generation *gen) {
     InterTree::traverse(gen->from, CTarget::sweep_for_locals, gen, NULL, LOCAL_IST);
 }
 
@@ -645,7 +592,7 @@ function togglePopup(material_id) {
     DISCARD_TEXT(T)
 }
 
-int CTarget::general_segment(code_generation_target *cgt, code_generation *gen, inter_tree_node *P) {
+int CTarget::general_segment(code_generation_target *cgt, code_generation *gen, inter_tree_node *P) {
     switch (P->W.data[ID_IFLD]) {
         case CONSTANT_IST: {
             inter_symbol *con_name =
@@ -665,28 +612,28 @@ function togglePopup(material_id) {
     return CTarget::default_segment(cgt);
 }
 
-int CTarget::default_segment(code_generation_target *cgt) {
+int CTarget::default_segment(code_generation_target *cgt) {
     return c_main_matter_I7CGS;
 }
-int CTarget::constant_segment(code_generation_target *cgt, code_generation *gen) {
+int CTarget::constant_segment(code_generation_target *cgt, code_generation *gen) {
     return c_early_matter_I7CGS;
 }
-int CTarget::basic_constant_segment(code_generation_target *cgt, code_generation *gen, int depth) {
+int CTarget::basic_constant_segment(code_generation_target *cgt, code_generation *gen, int depth) {
     if (depth >= 10) depth = 10;
     return c_constants_1_I7CGS + depth - 1;
 }
-int CTarget::property_segment(code_generation_target *cgt) {
+int CTarget::property_segment(code_generation_target *cgt) {
     return c_predeclarations_I7CGS;
 }
-int CTarget::tl_segment(code_generation_target *cgt) {
+int CTarget::tl_segment(code_generation_target *cgt) {
     return c_text_literals_code_I7CGS;
 }
 
-void CTarget::offer_pragma(code_generation_target *cgt, code_generation *gen,
+void CTarget::offer_pragma(code_generation_target *cgt, code_generation *gen,
     inter_tree_node *P, text_stream *tag, text_stream *content) {
 }
 
-void CTarget::mangle(code_generation_target *cgt, OUTPUT_STREAM, text_stream *identifier) {
+void CTarget::mangle(code_generation_target *cgt, OUTPUT_STREAM, text_stream *identifier) {
     if (Str::get_first_char(identifier) == '(') WRITE("%S", identifier);
     else if (Str::get_first_char(identifier) == '#') {
         WRITE("i7_mgl_sharp_");
@@ -696,14 +643,20 @@ function togglePopup(material_id) {
     } else WRITE("i7_mgl_%S", identifier);
 }
 
-int C_write_lookup_mode = FALSE;
-
-int CTarget::compile_primitive(code_generation_target *cgt, code_generation *gen,
+int CTarget::compile_primitive(code_generation_target *cgt, code_generation *gen,
     inter_symbol *prim_name, inter_tree_node *P) {
     text_stream *OUT = CodeGen::current(gen);
     int suppress_terminal_semicolon = FALSE;
     inter_tree *I = gen->from;
     inter_ti bip = Primitives::to_bip(I, prim_name);
+
+    int r = CReferences::compile_primitive(gen, bip, P);
+    if (r != NOT_APPLICABLE) return r;
+    r = CMemoryModel::compile_primitive(gen, bip, P);
+    if (r != NOT_APPLICABLE) return r;
+    r = CObjectModel::compile_primitive(gen, bip, P);
+    if (r != NOT_APPLICABLE) return r;
+
     switch (bip) {
         case INVERSION_BIP:     break;  we won't support this in C
 
@@ -736,34 +689,9 @@ function togglePopup(material_id) {
 
         case PUSH_BIP:          WRITE("i7_push("); INV_A1; WRITE(")"); break;
         case PULL_BIP:          INV_A1; WRITE(" = i7_pull()"); break;
-        case PREINCREMENT_BIP:  Generate primitive for store2.2; break;
-        case POSTINCREMENT_BIP: Generate primitive for store2.2; break;
-        case PREDECREMENT_BIP:  Generate primitive for store2.2; break;
-        case POSTDECREMENT_BIP: Generate primitive for store2.2; break;
-        case STORE_BIP:         Generate primitive for store2.2; break;
-        case SETBIT_BIP:        INV_A1; WRITE(" = "); INV_A1; WRITE(" | "); INV_A2; break;
-        case CLEARBIT_BIP:      INV_A1; WRITE(" = "); INV_A1; WRITE(" &~ ("); INV_A2; WRITE(")"); break;
-        case LOOKUP_BIP:        if (C_write_lookup_mode) {
-                                    C_write_lookup_mode = FALSE;
-                                    Generate primitive for lookupref2.4;
-                                } else {
-                                    Generate primitive for lookup2.3;
-                                }
-                                break;
-        case LOOKUPBYTE_BIP:    Generate primitive for lookupbyte2.5; break;
-        case LOOKUPREF_BIP:     Generate primitive for lookupref2.4; break;
-        case PROPERTYADDRESS_BIP: WRITE("i7_prop_addr("); INV_A1; WRITE(", "); INV_A2; WRITE(")"); break;
-        case PROPERTYLENGTH_BIP: WRITE("i7_prop_len("); INV_A1; WRITE(", "); INV_A2; WRITE(")"); break;
-        case PROPERTYVALUE_BIP: if (C_write_lookup_mode) {
-                                    C_write_lookup_mode = FALSE;
-                                    WRITE("i7_change_prop_value("); INV_A1; WRITE(", "); INV_A2; WRITE(", ");
-                                } else {
-                                    WRITE("i7_read_prop_value("); INV_A1; WRITE(", "); INV_A2; WRITE(")");
-                                }
-                                break;
         case BREAK_BIP:         WRITE("break"); break;
         case CONTINUE_BIP:      WRITE("continue"); break;
-        case RETURN_BIP:        Generate primitive for return2.6; break;
+        case RETURN_BIP:        Generate primitive for return2.2; break;
         case JUMP_BIP:          WRITE("goto "); INV_A1; break;
         case QUIT_BIP:          WRITE("exit(0)"); break;
         case RESTORE_BIP:       break;  we won't support this in C
@@ -787,13 +715,6 @@ function togglePopup(material_id) {
                                 WRITE("i7_call_5("); INV_A1; WRITE(", ");
                                 INV_A2; WRITE(", "); INV_A3; WRITE(", "); INV_A4; WRITE(", ");
                                 INV_A5; WRITE(", "); INV_A6; WRITE(")"); break;
-        case MESSAGE0_BIP:      WRITE("i7_call_0(i7_read_prop_value("); INV_A1; WRITE(", "); INV_A2; WRITE("))"); break;
-        case MESSAGE1_BIP:      WRITE("i7_call_1(i7_read_prop_value("); INV_A1; WRITE(", "); INV_A2; WRITE("), ");
-                                INV_A3; WRITE(")"); break;
-        case MESSAGE2_BIP:      WRITE("i7_call_2(i7_read_prop_value("); INV_A1; WRITE(", "); INV_A2; WRITE("), ");
-                                INV_A3; WRITE(", "); INV_A4; WRITE(")"); break;
-        case MESSAGE3_BIP:      WRITE("i7_call_3(i7_read_prop_value("); INV_A1; WRITE(", "); INV_A2; WRITE("), ");
-                                INV_A3; WRITE(", "); INV_A4; WRITE(", "); INV_A5; WRITE(")"); break;
         case CALLMESSAGE0_BIP:  WRITE("i7_ccall_0("); INV_A1; WRITE(")"); break;
         case CALLMESSAGE1_BIP:  WRITE("i7_ccall_1("); INV_A1; WRITE(", ");
                                 INV_A2; WRITE(")"); break;
@@ -814,12 +735,10 @@ function togglePopup(material_id) {
 
         case MOVE_BIP: WRITE("i7_move("); INV_A1; WRITE(", "); INV_A2; WRITE(")"); break;
         case REMOVE_BIP: WRITE("i7_move("); INV_A1; WRITE(", 0)"); break;
-        case GIVE_BIP: WRITE("i7_give("); INV_A1; WRITE(", "); INV_A2; WRITE(", 1)"); break;
-        case TAKE_BIP: WRITE("i7_give("); INV_A1; WRITE(", "); INV_A2; WRITE(", 0)"); break;
 
         case ALTERNATIVECASE_BIP: INV_A1; WRITE(", "); INV_A2; break;
         case SEQUENTIAL_BIP: WRITE("("); INV_A1; WRITE(","); INV_A2; WRITE(")"); break;
-        case TERNARYSEQUENTIAL_BIP: Generate primitive for ternarysequential2.7; break;
+        case TERNARYSEQUENTIAL_BIP: Generate primitive for ternarysequential2.3; break;
 
         case PRINT_BIP: WRITE("printf(\"%%s\", "); INV_A1_PRINTMODE; WRITE(")"); break;
         case PRINTRET_BIP: WRITE("printf(\"%%s\", "); INV_A1_PRINTMODE; WRITE("); return 1"); break;
@@ -837,19 +756,19 @@ function togglePopup(material_id) {
         case PRINTCINDEF_BIP: WRITE("i7_print_cindef_art("); INV_A1; WRITE(")"); break;
         case BOX_BIP: WRITE("i7_print_box("); INV_A1_BOXMODE; WRITE(")"); break;
 
-        case IF_BIP: Generate primitive for if2.8; break;
-        case IFDEBUG_BIP: Generate primitive for ifdebug2.9; break;
-        case IFSTRICT_BIP: Generate primitive for ifstrict2.10; break;
-        case IFELSE_BIP: Generate primitive for ifelse2.11; break;
-        case WHILE_BIP: Generate primitive for while2.12; break;
-        case DO_BIP: Generate primitive for do2.13; break;
-        case FOR_BIP: Generate primitive for for2.14; break;
-        case OBJECTLOOP_BIP: Generate primitive for objectloop2.15; break;
-        case OBJECTLOOPX_BIP: Generate primitive for objectloopx2.16; break;
-        case LOOP_BIP: Generate primitive for loop2.17; break;
-        case SWITCH_BIP: Generate primitive for switch2.18; break;
-        case CASE_BIP: Generate primitive for case2.19; break;
-        case DEFAULT_BIP: Generate primitive for default2.20; break;
+        case IF_BIP: Generate primitive for if2.4; break;
+        case IFDEBUG_BIP: Generate primitive for ifdebug2.5; break;
+        case IFSTRICT_BIP: Generate primitive for ifstrict2.6; break;
+        case IFELSE_BIP: Generate primitive for ifelse2.7; break;
+        case WHILE_BIP: Generate primitive for while2.8; break;
+        case DO_BIP: Generate primitive for do2.9; break;
+        case FOR_BIP: Generate primitive for for2.10; break;
+        case OBJECTLOOP_BIP: Generate primitive for objectloop2.11; break;
+        case OBJECTLOOPX_BIP: Generate primitive for objectloopx2.12; break;
+        case LOOP_BIP: Generate primitive for loop2.13; break;
+        case SWITCH_BIP: Generate primitive for switch2.14; break;
+        case CASE_BIP: Generate primitive for case2.15; break;
+        case DEFAULT_BIP: Generate primitive for default2.16; break;
 
         case RANDOM_BIP: WRITE("fn_i7_mgl_random(1, "); INV_A1; WRITE(")"); break;
 
@@ -867,60 +786,7 @@ function togglePopup(material_id) {
     CTarget::comparison(cgt, gen, bip, InterTree::first_child(P), InterTree::second_child(P));
 
-

§2.2. Generate primitive for store2.2 = -

- -
-    text_stream *store_form = NULL;
-    switch (bip) {
-        case PREINCREMENT_BIP:  store_form = I"i7_cpv_PREINC"; break;
-        case POSTINCREMENT_BIP: store_form = I"i7_cpv_POSTINC"; break;
-        case PREDECREMENT_BIP:  store_form = I"i7_cpv_PREDEC"; break;
-        case POSTDECREMENT_BIP: store_form = I"i7_cpv_POSTDEC"; break;
-        case STORE_BIP:         store_form = I"i7_cpv_SET"; break;
-    }
-    inter_tree_node *ref = InterTree::first_child(P);
-    if (CTarget::basically_an_array_write(gen->from, ref)) {
-        WRITE("("); C_write_lookup_mode = TRUE; INV_A1; C_write_lookup_mode = FALSE;
-        if (bip == STORE_BIP) { INV_A2; } else { WRITE("0"); }
-        WRITE(", %S))", store_form);
-    } else if (CTarget::basically_a_property_write(gen->from, ref)) {
-        WRITE("("); C_write_lookup_mode = TRUE; INV_A1; C_write_lookup_mode = FALSE;
-        if (bip == STORE_BIP) { INV_A2; } else { WRITE("0"); }
-        WRITE(", %S))", store_form);
-    } else {
-        switch (bip) {
-            case PREINCREMENT_BIP:  WRITE("++("); INV_A1; WRITE(")"); break;
-            case POSTINCREMENT_BIP: WRITE("("); INV_A1; WRITE(")++"); break;
-            case PREDECREMENT_BIP:  WRITE("--("); INV_A1; WRITE(")"); break;
-            case POSTDECREMENT_BIP: WRITE("("); INV_A1; WRITE(")--"); break;
-            case STORE_BIP:         WRITE("("); INV_A1; WRITE(" = "); INV_A2; WRITE(")"); break;
-        }
-    }
-
- -

§2.3. Generate primitive for lookup2.3 = -

- -
-    WRITE("i7_lookup(i7mem, "); INV_A1; WRITE(", "); INV_A2; WRITE(")");
-
- -

§2.4. Generate primitive for lookupref2.4 = -

- -
-    WRITE("write_i7_lookup(i7mem, "); INV_A1; WRITE(", "); INV_A2; WRITE(", ");
-
- -

§2.5. Generate primitive for lookupbyte2.5 = -

- -
-    WRITE("i7mem["); INV_A1; WRITE(" + "); INV_A2; WRITE("]");
-
- -

§2.6. Generate primitive for return2.6 = +

§2.2. Generate primitive for return2.2 =

@@ -941,7 +807,7 @@ function togglePopup(material_id) {
     }
 
-

§2.7. Generate primitive for ternarysequential2.7 = +

§2.3. Generate primitive for ternarysequential2.3 =

@@ -954,7 +820,7 @@ function togglePopup(material_id) {
     WRITE(")");
 
-

§2.8. Generate primitive for if2.8 = +

§2.4. Generate primitive for if2.4 =

@@ -963,7 +829,7 @@ function togglePopup(material_id) {
     suppress_terminal_semicolon = TRUE;
 
-

§2.9. Generate primitive for ifdebug2.9 = +

§2.5. Generate primitive for ifdebug2.5 =

@@ -971,7 +837,7 @@ function togglePopup(material_id) {
     suppress_terminal_semicolon = TRUE;
 
-

§2.10. Generate primitive for ifstrict2.10 = +

§2.6. Generate primitive for ifstrict2.6 =

@@ -979,7 +845,7 @@ function togglePopup(material_id) {
     suppress_terminal_semicolon = TRUE;
 
-

§2.11. Generate primitive for ifelse2.11 = +

§2.7. Generate primitive for ifelse2.7 =

@@ -988,7 +854,7 @@ function togglePopup(material_id) {
     suppress_terminal_semicolon = TRUE;
 
-

§2.12. Generate primitive for while2.12 = +

§2.8. Generate primitive for while2.8 =

@@ -996,14 +862,14 @@ function togglePopup(material_id) {
     suppress_terminal_semicolon = TRUE;
 
-

§2.13. Generate primitive for do2.13 = +

§2.9. Generate primitive for do2.9 =

     WRITE("do {"); INV_A2; WRITE("} until (\n"); INDENT; INV_A1; OUTDENT; WRITE(")\n");
 
-

§2.14. Generate primitive for for2.14 = +

§2.10. Generate primitive for for2.10 =

@@ -1020,7 +886,7 @@ function togglePopup(material_id) {
     suppress_terminal_semicolon = TRUE;
 
-

§2.15. Generate primitive for objectloop2.15 = +

§2.11. Generate primitive for objectloop2.11 =

@@ -1045,7 +911,7 @@ function togglePopup(material_id) {
     suppress_terminal_semicolon = TRUE;
 
-

§2.16. Generate primitive for objectloopx2.16 = +

§2.12. Generate primitive for objectloopx2.12 =

@@ -1059,7 +925,7 @@ function togglePopup(material_id) {
     suppress_terminal_semicolon = TRUE;
 
-

§2.17. Generate primitive for loop2.17 = +

§2.13. Generate primitive for loop2.13 =

@@ -1067,7 +933,7 @@ function togglePopup(material_id) {
     suppress_terminal_semicolon = TRUE;
 
-

§2.18. Generate primitive for switch2.18 = +

§2.14. Generate primitive for switch2.14 =

@@ -1076,7 +942,7 @@ function togglePopup(material_id) {
     suppress_terminal_semicolon = TRUE;
 
-

§2.19. Generate primitive for case2.19 = +

§2.15. Generate primitive for case2.15 =

@@ -1085,7 +951,7 @@ function togglePopup(material_id) {
     suppress_terminal_semicolon = TRUE;
 
-

§2.20. Generate primitive for default2.20 = +

§2.16. Generate primitive for default2.16 =

@@ -1096,7 +962,7 @@ function togglePopup(material_id) {
 

§3.

-void CTarget::caser(code_generation_target *cgt, code_generation *gen, inter_tree_node *X) {
+void CTarget::caser(code_generation_target *cgt, code_generation *gen, inter_tree_node *X) {
     if (X->W.data[ID_IFLD] == INV_IST) {
         if (X->W.data[METHOD_INV_IFLD] == INVOKED_PRIMITIVE) {
             inter_symbol *prim = Inter::Inv::invokee(X);
@@ -1114,7 +980,7 @@ function togglePopup(material_id) {
     WRITE(": ");
 }
 
-void CTarget::comparison(code_generation_target *cgt, code_generation *gen,
+void CTarget::comparison(code_generation_target *cgt, code_generation *gen,
     inter_ti bip, inter_tree_node *X, inter_tree_node *Y) {
     CTarget::comparison_r(cgt, gen, bip, X, Y, 0);
 }
@@ -1170,7 +1036,7 @@ function togglePopup(material_id) {
 

§4.

-void CTarget::compile_dictionary_word(code_generation_target *cgt, code_generation *gen,
+void CTarget::compile_dictionary_word(code_generation_target *cgt, code_generation *gen,
     text_stream *S, int pluralise) {
     text_stream *OUT = CodeGen::current(gen);
     text_stream *val = Dictionaries::get_text(C_GEN_DATA(C_vm_dictionary), S);
@@ -1187,7 +1053,7 @@ function togglePopup(material_id) {
 

§5.

-void CTarget::compile_literal_number(code_generation_target *cgt,
+void CTarget::compile_literal_number(code_generation_target *cgt,
     code_generation *gen, inter_ti val, int hex_mode) {
     text_stream *OUT = CodeGen::current(gen);
     if (hex_mode) WRITE("0x%x", val);
@@ -1197,7 +1063,7 @@ function togglePopup(material_id) {
 

§6.

-void CTarget::compile_literal_text(code_generation_target *cgt, code_generation *gen,
+void CTarget::compile_literal_text(code_generation_target *cgt, code_generation *gen,
     text_stream *S, int printing_mode, int box_mode) {
     text_stream *OUT = CodeGen::current(gen);
 
@@ -1236,7 +1102,7 @@ function togglePopup(material_id) {
 

§7.

-int CTarget::prepare_variable(code_generation_target *cgt, code_generation *gen,
+int CTarget::prepare_variable(code_generation_target *cgt, code_generation *gen,
     inter_tree_node *P, inter_symbol *var_name, int k) {
     if (Inter::Symbols::read_annotation(var_name, EXPLICIT_VARIABLE_IANN) != 1) {
         if (Inter::Symbols::read_annotation(var_name, ASSIMILATED_IANN) != 1) {
@@ -1251,7 +1117,7 @@ function togglePopup(material_id) {
     return k;
 }
 
-int CTarget::declare_variable(code_generation_target *cgt, code_generation *gen,
+int CTarget::declare_variable(code_generation_target *cgt, code_generation *gen,
     inter_tree_node *P, inter_symbol *var_name, int k, int of) {
     if (Inter::Symbols::read_annotation(var_name, ASSIMILATED_IANN) == 1) {
         generated_segment *saved = CodeGen::select(gen, c_globals_array_I7CGS);
@@ -1267,26 +1133,26 @@ function togglePopup(material_id) {
         CodeGen::deselect(gen, saved);
     }
     if (Inter::Symbols::read_annotation(var_name, EXPLICIT_VARIABLE_IANN) != 1) {
-        if (k == 0) CTarget::begin_array(cgt, gen, I"Global_Vars", WORD_ARRAY_FORMAT);
+        if (k == 0) CMemoryModel::begin_array(cgt, gen, I"Global_Vars", WORD_ARRAY_FORMAT);
         TEMPORARY_TEXT(val)
         CodeGen::select_temporary(gen, val);
         CodeGen::CL::literal(gen, NULL, Inter::Packages::scope_of(P), P->W.data[VAL1_VAR_IFLD], P->W.data[VAL2_VAR_IFLD], FALSE);
         CodeGen::deselect_temporary(gen);
-        CTarget::array_entry(cgt, gen, val, WORD_ARRAY_FORMAT);
+        CMemoryModel::array_entry(cgt, gen, val, WORD_ARRAY_FORMAT);
         DISCARD_TEXT(val)
         k++;
         if (k == of) {
             if (k < 2) {
-                CTarget::array_entry(cgt, gen, I"0", WORD_ARRAY_FORMAT);
-                CTarget::array_entry(cgt, gen, I"0", WORD_ARRAY_FORMAT);
+                CMemoryModel::array_entry(cgt, gen, I"0", WORD_ARRAY_FORMAT);
+                CMemoryModel::array_entry(cgt, gen, I"0", WORD_ARRAY_FORMAT);
             }
-            CTarget::end_array(cgt, gen, WORD_ARRAY_FORMAT);
+            CMemoryModel::end_array(cgt, gen, WORD_ARRAY_FORMAT);
         }
     }
     return k;
 }
 
-void CTarget::begin_constant(code_generation_target *cgt, code_generation *gen, text_stream *const_name, int continues, int ifndef_me) {
+void CTarget::begin_constant(code_generation_target *cgt, code_generation *gen, text_stream *const_name, int continues, int ifndef_me) {
     text_stream *OUT = CodeGen::current(gen);
     if (ifndef_me) {
         WRITE("#ifndef ");
@@ -1297,7 +1163,7 @@ function togglePopup(material_id) {
     CTarget::mangle(cgt, OUT, const_name);
     if (continues) WRITE(" ");
 }
-void CTarget::end_constant(code_generation_target *cgt, code_generation *gen, text_stream *const_name, int ifndef_me) {
+void CTarget::end_constant(code_generation_target *cgt, code_generation *gen, text_stream *const_name, int ifndef_me) {
     text_stream *OUT = CodeGen::current(gen);
     WRITE("\n");
     if (ifndef_me) WRITE("#endif\n");
@@ -1330,7 +1196,7 @@ function togglePopup(material_id) {
     CodeGen::deselect(gen, saved);
 }
 
-void CTarget::begin_functions(code_generation *gen) {
+void CTarget::begin_functions(code_generation *gen) {
     CTarget::add_main(gen);
     CTarget::make_veneer_fcf(gen, I"Z__Region");
     CTarget::make_veneer_fcf(gen, I"CP__Tab");
@@ -1359,7 +1225,7 @@ function togglePopup(material_id) {
 
 final_c_function *C_fn_being_found = NULL;
 
-void CTarget::begin_function(code_generation_target *cgt, int pass, code_generation *gen, inter_symbol *fn) {
+void CTarget::begin_function(code_generation_target *cgt, int pass, code_generation *gen, inter_symbol *fn) {
     text_stream *fn_name = CodeGen::CL::name(fn);
     C_fn_parameter_count = 0;
     if (pass == 1) {
@@ -1380,7 +1246,7 @@ function togglePopup(material_id) {
     }
 }
 
-void CTarget::begin_function_code(code_generation_target *cgt, code_generation *gen) {
+void CTarget::begin_function_code(code_generation_target *cgt, code_generation *gen) {
     text_stream *OUT = CodeGen::current(gen);
     WRITE(") {");
     if (C_fn_being_found) {
@@ -1390,7 +1256,7 @@ function togglePopup(material_id) {
     }
 }
 
-void CTarget::place_label(code_generation_target *cgt, code_generation *gen, text_stream *label_name) {
+void CTarget::place_label(code_generation_target *cgt, code_generation *gen, text_stream *label_name) {
     text_stream *OUT = CodeGen::current(gen);
     LOOP_THROUGH_TEXT(pos, label_name)
         if (Str::get(pos) != '.')
@@ -1398,7 +1264,7 @@ function togglePopup(material_id) {
     WRITE(": ;\n", label_name);
 }
 
-void CTarget::end_function(code_generation_target *cgt, int pass, code_generation *gen, inter_symbol *fn) {
+void CTarget::end_function(code_generation_target *cgt, int pass, code_generation *gen, inter_symbol *fn) {
     if (pass == 1) {
         WRITE_TO(C_fn_prototype, ")");
 
@@ -1417,7 +1283,7 @@ function togglePopup(material_id) {
     }
 }
 
-void CTarget::begin_function_call(code_generation_target *cgt, code_generation *gen, inter_symbol *fn, int argc) {
+void CTarget::begin_function_call(code_generation_target *cgt, code_generation *gen, inter_symbol *fn, int argc) {
     inter_tree_node *D = fn->definition;
     if ((D) && (D->W.data[ID_IFLD] == CONSTANT_IST) && (D->W.data[FORMAT_CONST_IFLD] == CONSTANT_DIRECT)) {
         inter_ti val1 = D->W.data[DATA_CONST_IFLD];
@@ -1440,7 +1306,7 @@ function togglePopup(material_id) {
         }
     }
 }
-void CTarget::argument(code_generation_target *cgt, code_generation *gen, inter_tree_node *F, inter_symbol *fn, int argc, int of_argc) {
+void CTarget::argument(code_generation_target *cgt, code_generation *gen, inter_tree_node *F, inter_symbol *fn, int argc, int of_argc) {
     text_stream *OUT = CodeGen::current(gen);
     if (GENERAL_POINTER_IS_NULL(fn->translation_data) == FALSE) {
         final_c_function *fcf = RETRIEVE_POINTER_final_c_function(fn->translation_data);
@@ -1451,7 +1317,7 @@ function togglePopup(material_id) {
         CodeGen::FC::frame(gen, F);
     }
 }
-void CTarget::end_function_call(code_generation_target *cgt, code_generation *gen, inter_symbol *fn, int argc) {
+void CTarget::end_function_call(code_generation_target *cgt, code_generation *gen, inter_symbol *fn, int argc) {
     if (GENERAL_POINTER_IS_NULL(fn->translation_data)) {
         text_stream *OUT = CodeGen::current(gen);
         WRITE(")");
@@ -1478,7 +1344,7 @@ function togglePopup(material_id) {
 
 int C_operand_count = 0, C_operand_branches = FALSE; inter_tree_node *C_operand_label = NULL;
 int C_pointer_on_operand = -1;
-void CTarget::begin_opcode(code_generation_target *cgt, code_generation *gen, text_stream *opcode) {
+void CTarget::begin_opcode(code_generation_target *cgt, code_generation *gen, text_stream *opcode) {
     text_stream *OUT = CodeGen::current(gen);
     C_operand_branches = FALSE;
     C_operand_label = NULL;
@@ -1511,7 +1377,7 @@ function togglePopup(material_id) {
     if (Str::eq(opcode, I"@tan")) C_pointer_on_operand = 2;
 
 }
-void CTarget::supply_operand(code_generation_target *cgt, code_generation *gen, inter_tree_node *F, int is_label) {
+void CTarget::supply_operand(code_generation_target *cgt, code_generation *gen, inter_tree_node *F, int is_label) {
     text_stream *OUT = CodeGen::current(gen);
     if (is_label) {
         C_operand_label = F;
@@ -1530,7 +1396,7 @@ function togglePopup(material_id) {
         }
     }
 }
-void CTarget::end_opcode(code_generation_target *cgt, code_generation *gen) {
+void CTarget::end_opcode(code_generation_target *cgt, code_generation *gen) {
     text_stream *OUT = CodeGen::current(gen);
     WRITE(")");
     if (C_operand_branches) {
@@ -1541,7 +1407,7 @@ function togglePopup(material_id) {
     }
 }
 
-void CTarget::declare_local_variable(code_generation_target *cgt, int pass,
+void CTarget::declare_local_variable(code_generation_target *cgt, int pass,
     code_generation *gen, inter_tree_node *P, inter_symbol *var_name) {
     TEMPORARY_TEXT(name)
     CTarget::mangle(cgt, name, CodeGen::CL::name(var_name));
@@ -1570,96 +1436,17 @@ function togglePopup(material_id) {
     DISCARD_TEXT(name)
 }
 
-int C_array_entry_count = 0;
-text_stream *C_array_name = NULL;
 
-void CTarget::begin_array(code_generation_target *cgt, code_generation *gen, text_stream *array_name, int format) {
+void CTarget::new_fake_action(code_generation_target *cgt, code_generation *gen, text_stream *name) {
     generated_segment *saved = CodeGen::select(gen, c_predeclarations_I7CGS);
     text_stream *OUT = CodeGen::current(gen);
-    WRITE("#define ");
-    CTarget::mangle(cgt, OUT, array_name);
-    WRITE(" %d  An array in format %d\n", C_GEN_DATA(extent_of_i7mem), format);
-    CodeGen::deselect(gen, saved);
-
-    if (C_array_name == NULL) C_array_name = Str::new();
-    Str::clear(C_array_name); WRITE_TO(C_array_name, "%S", array_name);
-    C_array_entry_count = 0;
-
-    if ((format == TABLE_ARRAY_FORMAT) || (format == BUFFER_ARRAY_FORMAT)) {
-        TEMPORARY_TEXT(extname)
-        WRITE_TO(extname, "xt_%S", array_name);
-        CTarget::array_entry(cgt, gen, extname, format);
-        DISCARD_TEXT(extname)
-    }
-}
-
-void CTarget::array_entry(code_generation_target *cgt, code_generation *gen, text_stream *entry, int format) {
-    generated_segment *saved = CodeGen::select(gen, c_mem_I7CGS);
-    text_stream *OUT = CodeGen::current(gen);
-    if ((format == TABLE_ARRAY_FORMAT) || (format == WORD_ARRAY_FORMAT)) {
-        WRITE("    I7BYTE_0(%S), I7BYTE_1(%S), I7BYTE_2(%S), I7BYTE_3(%S),\n",
-            entry, entry, entry, entry);
-        C_GEN_DATA(extent_of_i7mem) += 4;
-    } else {
-        WRITE("    (i7byte) %S,\n", entry);
-        C_GEN_DATA(extent_of_i7mem) += 1;
-    }
-    CodeGen::deselect(gen, saved);
-    C_array_entry_count++;
-}
-
-void CTarget::end_array(code_generation_target *cgt, code_generation *gen, int format) {
-    generated_segment *saved = CodeGen::select(gen, c_predeclarations_I7CGS);
-    text_stream *OUT = CodeGen::current(gen);
-    if ((format == TABLE_ARRAY_FORMAT) || (format == BUFFER_ARRAY_FORMAT)) {
-        WRITE("#define xt_%S %d\n", C_array_name, C_array_entry_count-1);
-    }
-    CodeGen::deselect(gen, saved);
-}
-
-int CTarget::basically_an_array_write(inter_tree *I, inter_tree_node *P) {
-    int reffed = FALSE;
-    while (P->W.data[ID_IFLD] == REFERENCE_IST) {
-        P = InterTree::first_child(P);
-        reffed = TRUE;
-    }
-    if (P->W.data[ID_IFLD] == INV_IST) {
-        if (P->W.data[METHOD_INV_IFLD] == INVOKED_PRIMITIVE) {
-            inter_symbol *prim = Inter::Inv::invokee(P);
-            inter_ti bip = Primitives::to_bip(I, prim);
-            if (bip == LOOKUPREF_BIP) return TRUE;
-            if ((bip == LOOKUP_BIP) && (reffed)) return TRUE;
-        }
-    }
-    return FALSE;
-}
-
-int CTarget::basically_a_property_write(inter_tree *I, inter_tree_node *P) {
-    int reffed = FALSE;
-    while (P->W.data[ID_IFLD] == REFERENCE_IST) {
-        P = InterTree::first_child(P);
-        reffed = TRUE;
-    }
-    if (P->W.data[ID_IFLD] == INV_IST) {
-        if (P->W.data[METHOD_INV_IFLD] == INVOKED_PRIMITIVE) {
-            inter_symbol *prim = Inter::Inv::invokee(P);
-            inter_ti bip = Primitives::to_bip(I, prim);
-            if (bip == PROPERTYVALUE_BIP) return TRUE;
-        }
-    }
-    return FALSE;
-}
-
-void CTarget::new_fake_action(code_generation_target *cgt, code_generation *gen, text_stream *name) {
-    generated_segment *saved = CodeGen::select(gen, c_predeclarations_I7CGS);
-    text_stream *OUT = CodeGen::current(gen);
-    WRITE("#define i7_ss_%S %d\n", name, C_GEN_DATA(C_action_count)++);
-    CodeGen::deselect(gen, saved);
-}
+    WRITE("#define i7_ss_%S %d\n", name, C_GEN_DATA(C_action_count)++);
+    CodeGen::deselect(gen, saved);
+}
 
-
  • The function CTarget::array_entry is used in Generating C (§1).
  • The function CTarget::end_array is used in Generating C (§1).
  • The function CTarget::basically_an_array_write is used in §2.2.
  • The function CTarget::basically_a_property_write is used in §2.2.
  • The function CTarget::new_fake_action is used in Generating C (§1).
  • The structure final_c_function is private to this section.
+
  • The structure final_c_function is private to this section.
diff --git a/docs/final-module/5-cmm.html b/docs/final-module/5-cmm.html new file mode 100644 index 000000000..198ae8735 --- /dev/null +++ b/docs/final-module/5-cmm.html @@ -0,0 +1,409 @@ + + + + C Memory Model + + + + + + + + + + + + + + + + + + + +
+ + +

How arrays of all kinds are stored in C.

+ +
+ +

§1. Setting up the model.

+ +
+void CMemoryModel::initialise(code_generation_target *cgt) {
+    METHOD_ADD(cgt, BEGIN_ARRAY_MTID, CMemoryModel::begin_array);
+    METHOD_ADD(cgt, ARRAY_ENTRY_MTID, CMemoryModel::array_entry);
+    METHOD_ADD(cgt, END_ARRAY_MTID, CMemoryModel::end_array);
+}
+
+typedef struct C_generation_memory_model_data {
+    int himem;  high point of memory: 1 more than the largest legal address
+    struct text_stream *array_name;
+    int entry_count;
+    int next_node_is_a_ref;
+} C_generation_memory_model_data;
+
+void CMemoryModel::initialise_data(code_generation *gen) {
+    C_GEN_DATA(memdata.himem) = 0;
+    C_GEN_DATA(memdata.array_name) = Str::new();
+    C_GEN_DATA(memdata.entry_count) = 0;
+    C_GEN_DATA(memdata.next_node_is_a_ref) = FALSE;
+}
+
+
  • The structure C_generation_memory_model_data is accessed in 5/crf and here.
+

§2. Byte-addressable memory. The Inter semantics require that there be an area of byte-accessible memory: +

+ +
  • (a) Byte-accessible memory must contain all of the arrays. These can but need +not have alignment gaps in between them. (For C, they do not.) +
  • (b) "Addresses" in this memory identify individual byte positions in it. These +can but need not start at 0. (For C, they do.) They must not be too large to +fit into an Inter value. +
  • (c) When an array name is compiled, its runtime value must be its address. +
  • (d) When an Inter value is stored in byte-accessible memory, it occupies either +2 or 4 consecutive bytes, with the little end first. The result is called a +"word". (For C, always 4, which is always sizeof(i7val).) Conversion between +a word stored in memory and an Inter value must be faithful in both directions. +
  • (e) Words can be stored at any byte position, and not only at (say) multiples +of 2 or 4. +
  • (f) Arrays in memory are free to contain a mixture of bytes and words: some do. +
  • (g) Data may be written in byte form and read back in word form, or vice versa. +
+

We will manage that with a single C array. This is first predeclared here: +

+ +
+i7byte i7mem[];
+
+
  • This is part of the extract file inform7_clib.h.
+

§3. Declaring that array is our main task in this section. +

+ +
+void CMemoryModel::begin(code_generation *gen) {
+    generated_segment *saved = CodeGen::select(gen, c_mem_I7CGS);
+    text_stream *OUT = CodeGen::current(gen);
+    WRITE("i7byte i7mem[] = {\n");
+    CodeGen::deselect(gen, saved);
+}
+
+

§4. We will end the array with two dummy bytes (which should never be accessed) +just in case, and to ensure that it is never empty, which would be illegal +in C. +

+ +
+void CMemoryModel::end(code_generation *gen) {
+    generated_segment *saved = CodeGen::select(gen, c_mem_I7CGS);
+    text_stream *OUT = CodeGen::current(gen);
+    WRITE("0, 0 };\n");
+    CodeGen::deselect(gen, saved);
+}
+
+

§5. Reading and writing memory. Given the above array, it's easy to read and write bytes: if a is the address +then we can simply refer to i7mem[a]. Words are more challenging since we +need to pack and unpack them. +

+ +

The following function reads a word which is in entry array_index (counting +0, 1, 2, ...) in the array which begins at the byte address array_address in +the bank of memory data. In practice, we will only every use this function +with data set to i7mem. +

+ +

The equivalent for reading a byte entry is data[array_address + array_index]. +

+ +
+i7val i7_read_word(i7byte data[], i7val array_address, i7val array_index) {
+    int byte_position = array_address + 4*array_index;
+    return             (i7val) data[byte_position]      +
+                0x100*((i7val) data[byte_position + 1]) +
+              0x10000*((i7val) data[byte_position + 2]) +
+            0x1000000*((i7val) data[byte_position + 3]);
+}
+
+
  • This is part of the extract file inform7_clib.h.
+

Now for writing. Memory locations (like variables and property values) can not +only be "set", that is, written to, but also pre-decremented, post-decremented, +pre-incremented and post-incremented. This is the equivalent of being able to +apply ++ or -- either side, in C terms. The following enumerated constants +identify which of these five operations to perform: +

+ +
+#define i7_lvalue_SET 1
+#define i7_lvalue_PREDEC 2
+#define i7_lvalue_POSTDEC 3
+#define i7_lvalue_PREINC 4
+#define i7_lvalue_POSTINC 5
+#define i7_lvalue_SETBIT 6
+#define i7_lvalue_CLEARBIT 7
+
+
  • This is part of the extract file inform7_clib.h.
+

§6. Packing, unlike unpacking, is done with macros so that it is possible to +express a packed word in constant context, which we will need later. +

+ +
+#define I7BYTE_3(V) ((V & 0xFF000000) >> 24)
+#define I7BYTE_2(V) ((V & 0x00FF0000) >> 16)
+#define I7BYTE_1(V) ((V & 0x0000FF00) >> 8)
+#define I7BYTE_0(V)  (V & 0x000000FF)
+
+i7val i7_write_word(i7byte data[], i7val array_address, i7val array_index, i7val new_val, int way) {
+    i7val old_val = i7_read_word(data, array_address, array_index);
+    i7val return_val = new_val;
+    switch (way) {
+        case i7_lvalue_PREDEC:   return_val = old_val;   new_val = old_val-1; break;
+        case i7_lvalue_POSTDEC:  return_val = old_val-1; new_val = old_val-1; break;
+        case i7_lvalue_PREINC:   return_val = old_val;   new_val = old_val+1; break;
+        case i7_lvalue_POSTINC:  return_val = old_val+1; new_val = old_val+1; break;
+        case i7_lvalue_SETBIT:   new_val = old_val | new_val; return_val = new_val; break;
+        case i7_lvalue_CLEARBIT: new_val = old_val &(~new_val); return_val = new_val; break;
+    }
+    int byte_position = array_address + 4*array_index;
+    data[byte_position]   = I7BYTE_0(new_val);
+    data[byte_position+1] = I7BYTE_1(new_val);
+    data[byte_position+2] = I7BYTE_2(new_val);
+    data[byte_position+3] = I7BYTE_3(new_val);
+    return return_val;
+}
+
+
  • This is part of the extract file inform7_clib.h.
+

§7. Populating memory with arrays. Inter supports four sorts of arrays, with behaviour as laid out in this 2x2 grid: +

+ +
+             | entries count 0, 1, 2,...     | entry 0 is N, then entries count 1, 2, ..., N
+-------------+-------------------------------+-----------------------------------------------
+byte entries | BYTE_ARRAY_FORMAT             | BUFFER_ARRAY_FORMAT
+-------------+-------------------------------+-----------------------------------------------
+word entries | WORD_ARRAY_FORMAT             | TABLE_ARRAY_FORMAT
+-------------+-------------------------------+-----------------------------------------------
+
+
+void CMemoryModel::begin_array(code_generation_target *cgt, code_generation *gen,
+    text_stream *array_name, int format) {
+    Str::clear(C_GEN_DATA(memdata.array_name));
+    WRITE_TO(C_GEN_DATA(memdata.array_name), "%S", array_name);
+    C_GEN_DATA(memdata.entry_count) = 0;
+
+    text_stream *format_name = I"unknown";
+    Work out the format name7.1;
+    Define a constant for the byte address in memory where the array begins7.2;
+    if ((format == TABLE_ARRAY_FORMAT) || (format == BUFFER_ARRAY_FORMAT))
+        Place the extent entry N at index 07.3;
+}
+
+

§7.1. Work out the format name7.1 = +

+ +
+    switch (format) {
+        case BYTE_ARRAY_FORMAT: format_name = I"byte"; break;
+        case WORD_ARRAY_FORMAT: format_name = I"word"; break;
+        case BUFFER_ARRAY_FORMAT: format_name = I"buffer"; break;
+        case TABLE_ARRAY_FORMAT: format_name = I"table"; break;
+    }
+
+
  • This code is used in §7.
+

§7.2. Crucially, the array names are #define constants declared up at the top +of the source code: they are not variables with pointer types, or something +like that. This means they can legally be used as values elsewhere in i7mem, +or as initial values of variables, and so on. +

+ +

Object, class and function names can also legally appear as array entries, +because they too are defined constants, equal to their IDs: see C Object Model. +

+ +

Define a constant for the byte address in memory where the array begins7.2 = +

+ +
+    generated_segment *saved = CodeGen::select(gen, c_predeclarations_I7CGS);
+    text_stream *OUT = CodeGen::current(gen);
+    WRITE("#define ");
+    CTarget::mangle(cgt, OUT, array_name);
+    WRITE(" %d /* = position in i7mem of %S array %S */\n",
+        C_GEN_DATA(memdata.himem), format_name, array_name);
+    CodeGen::deselect(gen, saved);
+
+
  • This code is used in §7.
+

§7.3. Of course, right now we don't know N, the extent of the array. So we will +refer to this with a constant like xt_myarray, which we will retrospectively +predefine when the array ends. +

+ +

Place the extent entry N at index 07.3 = +

+ +
+    TEMPORARY_TEXT(extname)
+    WRITE_TO(extname, "xt_%S", array_name);
+    CMemoryModel::array_entry(cgt, gen, extname, format);
+    DISCARD_TEXT(extname)
+
+
  • This code is used in §7.
+

§8. The call to CMemoryModel::begin_array is then followed by a series of calls to: +

+ +
+void CMemoryModel::array_entry(code_generation_target *cgt, code_generation *gen,
+    text_stream *entry, int format) {
+    generated_segment *saved = CodeGen::select(gen, c_mem_I7CGS);
+    text_stream *OUT = CodeGen::current(gen);
+    if ((format == TABLE_ARRAY_FORMAT) || (format == WORD_ARRAY_FORMAT))
+        This is a word entry8.2
+    else
+        This is a byte entry8.1;
+    CodeGen::deselect(gen, saved);
+    C_GEN_DATA(memdata.entry_count)++;
+}
+
+

§8.1. This is a byte entry8.1 = +

+ +
+    WRITE("    (i7byte) %S,\n", entry);
+    C_GEN_DATA(memdata.himem) += 1;
+
+
  • This code is used in §8.
+

§8.2. Now we see why it was important for I7BYTE_0 and so on to be macros: they +use only arithmetic operations which can be constant-folded by the C compiler, +and therefore if X is a valid constant-context expression in C then so is +I7BYTE_0(X). +

+ +

This is a word entry8.2 = +

+ +
+    WRITE("    I7BYTE_0(%S), I7BYTE_1(%S), I7BYTE_2(%S), I7BYTE_3(%S),\n",
+        entry, entry, entry, entry);
+    C_GEN_DATA(memdata.himem) += 4;
+
+
  • This code is used in §8.
+

§9. When all the entries have been placed, the following is called. It does nothing +except to predeclare the extent constant, if one was used. +

+ +
+void CMemoryModel::end_array(code_generation_target *cgt, code_generation *gen, int format) {
+    if ((format == TABLE_ARRAY_FORMAT) || (format == BUFFER_ARRAY_FORMAT)) {
+        generated_segment *saved = CodeGen::select(gen, c_predeclarations_I7CGS);
+        text_stream *OUT = CodeGen::current(gen);
+        WRITE("#define xt_%S %d\n",
+            C_GEN_DATA(memdata.array_name), C_GEN_DATA(memdata.entry_count)-1);
+        CodeGen::deselect(gen, saved);
+    }
+}
+
+

§10. Primitives for byte and word lookup. The signatures here are: +

+ +
+primitive !lookup val val -> val
+primitive !lookupbyte val val -> val
+primitive !lookupref val val -> ref
+
+

!lookupref is a slightly redundant abbreviation for reference with !lookup +as a child. At present, there is no equivalent !lookupbyteref. +

+ +
+int CMemoryModel::compile_primitive(code_generation *gen, inter_ti bip, inter_tree_node *P) {
+    text_stream *OUT = CodeGen::current(gen);
+    switch (bip) {
+        case LOOKUP_BIP:    if (CReferences::am_I_a_ref(gen)) {
+                                Word value as reference10.3;
+                            } else {
+                                Word value as value10.1;
+                            }
+                            break;
+        case LOOKUPBYTE_BIP: Byte value as value10.2; break;
+        case LOOKUPREF_BIP:  Word value as reference10.3; break;
+        default: return NOT_APPLICABLE;
+    }
+    return FALSE;
+}
+
+

§10.1. Word value as value10.1 = +

+ +
+    WRITE("i7_read_word(i7mem, "); INV_A1; WRITE(", "); INV_A2; WRITE(")");
+
+
  • This code is used in §10.
+

§10.2. Byte value as value10.2 = +

+ +
+    WRITE("i7mem["); INV_A1; WRITE(" + "); INV_A2; WRITE("]");
+
+
  • This code is used in §10.
+

§10.3. Word value as reference10.3 = +

+ +
+    WRITE("i7_write_word(i7mem, "); INV_A1; WRITE(", "); INV_A2; WRITE(", ");
+
+
  • This code is used in §10 (twice).
+ + +
+ + + diff --git a/docs/final-module/5-com.html b/docs/final-module/5-com.html index c04bf9650..50f2fda1d 100644 --- a/docs/final-module/5-com.html +++ b/docs/final-module/5-com.html @@ -74,7 +74,7 @@ function togglePopup(material_id) {

How objects, classes and properties are compiled to C.

-
+

§1. Setting up the model.

@@ -104,7 +104,7 @@ function togglePopup(material_id) { int is_class; } C_property_owner; -void CObjectModel::initialise_data(code_generation *gen) { +void CObjectModel::initialise_data(code_generation *gen) { C_GEN_DATA(objdata.owner_id_count) = 0; C_GEN_DATA(objdata.property_id_counter) = 0; C_GEN_DATA(objdata.C_property_offsets_made) = 0; @@ -112,13 +112,13 @@ function togglePopup(material_id) { C_GEN_DATA(objdata.owners_capacity) = 0; } -void CObjectModel::begin(code_generation *gen) { +void CObjectModel::begin(code_generation *gen) { CObjectModel::initialise_data(gen); Begin the initialiser function1.5; CObjectModel::declare_property_by_name(gen, I"value_range", TRUE); } -void CObjectModel::end(code_generation *gen) { +void CObjectModel::end(code_generation *gen) { Complete the initialiser function1.6; Complete the property-offset creator function1.4; Predeclare the object count and class array1.3; @@ -369,12 +369,12 @@ properties at runtime. if (C_GEN_DATA(objdata.C_property_offsets_made)++ == 0) Begin the property-offset creator function8.1; - WRITE("write_i7_lookup(i7mem, "); + WRITE("i7_write_word(i7mem, "); if (as_attr) CTarget::mangle(cgt, OUT, I"attributed_property_offsets"); else CTarget::mangle(cgt, OUT, I"valued_property_offsets"); WRITE(", "); CTarget::mangle(cgt, OUT, prop); - WRITE(", %d, i7_cpv_SET);\n", pos); + WRITE(", %d, i7_lvalue_SET);\n", pos); CodeGen::deselect(gen, saved); }
@@ -389,9 +389,9 @@ not deriving from an Inform program.
     WRITE("i7val fn_i7_mgl_CreatePropertyOffsets(int argc) {\n"); INDENT;
     WRITE("for (int i=0; i<i7_mgl_attributed_property_offsets_SIZE; i++)\n"); INDENT;
-    WRITE("write_i7_lookup(i7mem, i7_mgl_attributed_property_offsets, i, -1, i7_cpv_SET);\n"); OUTDENT;
+    WRITE("i7_write_word(i7mem, i7_mgl_attributed_property_offsets, i, -1, i7_lvalue_SET);\n"); OUTDENT;
     WRITE("for (int i=0; i<i7_mgl_valued_property_offsets_SIZE; i++)\n"); INDENT;
-    WRITE("write_i7_lookup(i7mem, i7_mgl_valued_property_offsets, i, -1, i7_cpv_SET);\n"); OUTDENT;
+    WRITE("i7_write_word(i7mem, i7_mgl_valued_property_offsets, i, -1, i7_lvalue_SET);\n"); OUTDENT;
 
  • This code is used in §8.

§1.4. This function has no meaningful return value, but has to conform to our @@ -468,7 +468,40 @@ the next owner is declared. CodeGen::deselect(gen, saved); }

-

§10. Reading and writing properties. So here is the run-time storage for property values, and simple code to read +

§10. Primitives for property usage. The following primitives are all implemented by calling suitable C functions, +which we will then need to write in inform7_clib.h. +

+ +
+int CObjectModel::compile_primitive(code_generation *gen, inter_ti bip, inter_tree_node *P) {
+    text_stream *OUT = CodeGen::current(gen);
+    switch (bip) {
+        case PROPERTYADDRESS_BIP: WRITE("i7_prop_addr("); INV_A1; WRITE(", "); INV_A2; WRITE(")"); break;
+        case PROPERTYLENGTH_BIP: WRITE("i7_prop_len("); INV_A1; WRITE(", "); INV_A2; WRITE(")"); break;
+        case PROPERTYVALUE_BIP: if (CReferences::am_I_a_ref(gen)) {
+                                    WRITE("i7_change_prop_value("); INV_A1; WRITE(", "); INV_A2; WRITE(", ");
+                                } else {
+                                    WRITE("i7_read_prop_value("); INV_A1; WRITE(", "); INV_A2; WRITE(")");
+                                }
+                                break;
+        case MESSAGE0_BIP:      WRITE("i7_call_0(i7_read_prop_value("); INV_A1;
+                                WRITE(", "); INV_A2; WRITE("))"); break;
+        case MESSAGE1_BIP:      WRITE("i7_call_1(i7_read_prop_value("); INV_A1;
+                                WRITE(", "); INV_A2; WRITE("), "); INV_A3; WRITE(")"); break;
+        case MESSAGE2_BIP:      WRITE("i7_call_2(i7_read_prop_value("); INV_A1;
+                                WRITE(", "); INV_A2; WRITE("), ");
+                                INV_A3; WRITE(", "); INV_A4; WRITE(")"); break;
+        case MESSAGE3_BIP:      WRITE("i7_call_3(i7_read_prop_value("); INV_A1;
+                                WRITE(", "); INV_A2; WRITE("), ");
+                                INV_A3; WRITE(", "); INV_A4; WRITE(", "); INV_A5; WRITE(")"); break;
+        case GIVE_BIP:          WRITE("i7_give("); INV_A1; WRITE(", "); INV_A2; WRITE(", 1)"); break;
+        case TAKE_BIP:          WRITE("i7_give("); INV_A1; WRITE(", "); INV_A2; WRITE(", 0)"); break;
+        default: return NOT_APPLICABLE;
+    }
+    return FALSE;
+}
+
+

§11. Reading and writing properties. So here is the run-time storage for property values, and simple code to read and write them. Note that, unlike in the Z-machine or Glulx implementations, property values are not stored in the memory map.

@@ -480,14 +513,6 @@ property values are not stored in the memory map. } i7_property_set; i7_property_set i7_properties[i7_max_objects]; -i7val i7_read_prop_value(i7val owner_id, i7val prop_id) { - if ((owner_id <= 0) || (owner_id >= i7_max_objects) || - (prop_id < 0) || (prop_id >= i7_no_property_ids)) return 0; - while (i7_properties[(int) owner_id].value_set[(int) prop_id] == 0) - owner_id = i7_class_of[owner_id]; - return i7_properties[(int) owner_id].value[(int) prop_id]; -} - void i7_write_prop_value(i7val owner_id, i7val prop_id, i7val val) { if ((owner_id <= 0) || (owner_id >= i7_max_objects) || (prop_id < 0) || (prop_id >= i7_no_property_ids)) { @@ -499,17 +524,28 @@ property values are not stored in the memory map. }
  • This is part of the extract file inform7_clib.h.
-

§11. Other things to do with properties.

+

§12. And here sre the functions called by the above primitives: +

+i7val i7_read_prop_value(i7val owner_id, i7val prop_id) {
+    if ((owner_id <= 0) || (owner_id >= i7_max_objects) ||
+        (prop_id < 0) || (prop_id >= i7_no_property_ids)) return 0;
+    while (i7_properties[(int) owner_id].value_set[(int) prop_id] == 0)
+        owner_id = i7_class_of[owner_id];
+    return i7_properties[(int) owner_id].value[(int) prop_id];
+}
+
 i7val i7_change_prop_value(i7val obj, i7val pr, i7val to, int way) {
     i7val val = i7_read_prop_value(obj, pr), new_val = val;
     switch (way) {
-        case i7_cpv_SET:     i7_write_prop_value(obj, pr, to); new_val = to; break;
-        case i7_cpv_PREDEC:  new_val = val; i7_write_prop_value(obj, pr, val-1); break;
-        case i7_cpv_POSTDEC: new_val = val-1; i7_write_prop_value(obj, pr, new_val); break;
-        case i7_cpv_PREINC:  new_val = val; i7_write_prop_value(obj, pr, val+1); break;
-        case i7_cpv_POSTINC: new_val = val+1; i7_write_prop_value(obj, pr, new_val); break;
+        case i7_lvalue_SET:      i7_write_prop_value(obj, pr, to); new_val = to; break;
+        case i7_lvalue_PREDEC:   new_val = val; i7_write_prop_value(obj, pr, val-1); break;
+        case i7_lvalue_POSTDEC:  new_val = val-1; i7_write_prop_value(obj, pr, new_val); break;
+        case i7_lvalue_PREINC:   new_val = val; i7_write_prop_value(obj, pr, val+1); break;
+        case i7_lvalue_POSTINC:  new_val = val+1; i7_write_prop_value(obj, pr, new_val); break;
+        case i7_lvalue_SETBIT:   new_val = val | new_val; i7_write_prop_value(obj, pr, new_val); break;
+        case i7_lvalue_CLEARBIT: new_val = val &(~new_val); i7_write_prop_value(obj, pr, new_val); break;
     }
     return new_val;
 }
@@ -530,7 +566,7 @@ property values are not stored in the memory map.
 
  • This is part of the extract file inform7_clib.h.
diff --git a/docs/final-module/5-crf.html b/docs/final-module/5-crf.html new file mode 100644 index 000000000..3ebfcd177 --- /dev/null +++ b/docs/final-module/5-crf.html @@ -0,0 +1,227 @@ + + + + C References + + + + + + + + + + + + + + + + + + + + + + + +
+ + +

How changes to storage objects are translated into C.

+ +

§1. References identify storage objects which are being written to or otherwise +modified, rather than having their current contents read. +

+ +

There are seven possible ways to modify something identified by a reference, +and we need constants to identify these ways at runtime: +

+ +
+#define i7_lvalue_SET 1
+#define i7_lvalue_PREDEC 2
+#define i7_lvalue_POSTDEC 3
+#define i7_lvalue_PREINC 4
+#define i7_lvalue_POSTINC 5
+#define i7_lvalue_SETBIT 6
+#define i7_lvalue_CLEARBIT 7
+
+
  • This is part of the extract file inform7_clib.h.
+

§2. Those seven ways correspond to seven Inter primitives, with the following +signatures: +

+ +
+primitive !store         ref val -> val
+primitive !preincrement  ref -> val
+primitive !postincrement ref -> val
+primitive !predecrement  ref -> val
+primitive !postdecrement ref -> val
+primitive !setbit        ref val -> void
+primitive !clearbit      ref val -> void
+
+

Since C functions can have their return values freely ignored, we will in fact +implement !setbit and !clearbit as if they too had the signature +ref val -> val. +

+ +

For all these primitives, then, the first operand A1 is a ref, and the following +function should be used to generate from it: +

+ +
+void CReferences::A1_as_ref(code_generation *gen, inter_tree_node *P) {
+    C_GEN_DATA(memdata.next_node_is_a_ref) = TRUE;
+    CodeGen::FC::frame(gen, InterTree::first_child(P));
+    C_GEN_DATA(memdata.next_node_is_a_ref) = FALSE;
+}
+
+

§3. That sets a temporary mode which is immediately detected and cleared by +the generator for whatever A1 actually is. That generator is expected to call +this function to detect whether it's a ref. In this mode, A1 is compiled not +to a valid C expression to evaluate the contents of A1, but instead to a +function call which will modify A1, and which is missing one or two final +arguments. +

+ +

Note that the mode is auto-exited at once. This is all a bit clumsy, but is +correct. +

+ +
+int CReferences::am_I_a_ref(code_generation *gen) {
+    int answer = C_GEN_DATA(memdata.next_node_is_a_ref);
+    C_GEN_DATA(memdata.next_node_is_a_ref) = FALSE;
+    return answer;
+}
+
+

§4. So, then, here goes: +

+ +
+int CReferences::compile_primitive(code_generation *gen, inter_ti bip, inter_tree_node *P) {
+    text_stream *OUT = CodeGen::current(gen);
+    text_stream *store_form = NULL;
+    switch (bip) {
+        case STORE_BIP:         store_form = I"i7_lvalue_SET"; break;
+        case PREINCREMENT_BIP:  store_form = I"i7_lvalue_PREINC"; break;
+        case POSTINCREMENT_BIP: store_form = I"i7_lvalue_POSTINC"; break;
+        case PREDECREMENT_BIP:  store_form = I"i7_lvalue_PREDEC"; break;
+        case POSTDECREMENT_BIP: store_form = I"i7_lvalue_POSTDEC"; break;
+        case SETBIT_BIP:        store_form = I"i7_lvalue_SETBIT"; break;
+        case CLEARBIT_BIP:      store_form = I"i7_lvalue_CLEARBIT"; break;
+        default: return NOT_APPLICABLE;
+    }
+    if (store_form) This does indeed modify a value by reference4.1;
+    return FALSE;
+}
+
+

§4.1. Some storage objects, like variables, can be generated to C code which works +in either an lvalue or rvalue context. For example, the Inter variable frog +generates just as the C variable i7_mgl_frog.1 It's then fine to generate +code like either 10 + i7_mgl_frog, where it is used in a val context, or +like i7_mgl_frog++, where it is used in a ref context. +

+ +

But other storage objects are not so lucky, and those need to generate to +different function calls, one used in a ref setting, one used in a val. +That's what is done by the "A1 as ref" mode set up above. +

+ +
  • 1 In real life, do not mangle frogs. See C. S. Lewis, "Perelandra", 1943. +

+

This does indeed modify a value by reference4.1 = +

+ +
+    inter_tree_node *ref = InterTree::first_child(P);
+    if ((CodeGen::CL::node_is_word_array_ref(gen->from, ref)) ||
+        (CodeGen::CL::node_is_property_ref(gen->from, ref))) {
+        Handle the ref using the incomplete-function mode4.1.1;
+    } else {
+        Handle the ref with C code working either as lvalue or rvalue4.1.2;
+    }
+
+
  • This code is used in §4.
+

§4.1.1. Handle the ref using the incomplete-function mode4.1.1 = +

+ +
+    WRITE("("); CReferences::A1_as_ref(gen, P);
+    if (bip == STORE_BIP) { INV_A2; } else { WRITE("0"); }
+    WRITE(", %S))", store_form);
+
+
  • This code is used in §4.1.
+

§4.1.2. Handle the ref with C code working either as lvalue or rvalue4.1.2 = +

+ +
+    switch (bip) {
+        case PREINCREMENT_BIP:  WRITE("++("); INV_A1; WRITE(")"); break;
+        case POSTINCREMENT_BIP: WRITE("("); INV_A1; WRITE(")++"); break;
+        case PREDECREMENT_BIP:  WRITE("--("); INV_A1; WRITE(")"); break;
+        case POSTDECREMENT_BIP: WRITE("("); INV_A1; WRITE(")--"); break;
+        case STORE_BIP:         WRITE("("); INV_A1; WRITE(" = "); INV_A2; WRITE(")"); break;
+        case SETBIT_BIP:        INV_A1; WRITE(" = "); INV_A1; WRITE(" | "); INV_A2; break;
+        case CLEARBIT_BIP:      INV_A1; WRITE(" = "); INV_A1; WRITE(" &~ ("); INV_A2; WRITE(")"); break;
+    }
+
+
  • This code is used in §4.1.
+ + +
+ + + diff --git a/docs/final-module/5-fnc.html b/docs/final-module/5-fnc.html index 0085d5bc3..48687e7a3 100644 --- a/docs/final-module/5-fnc.html +++ b/docs/final-module/5-fnc.html @@ -21,6 +21,7 @@ function togglePopup(material_id) { + @@ -73,19 +74,19 @@ function togglePopup(material_id) {

To generate I6 code from intermediate code.

-
+
-

§1. Target, -

+

§1. Target.

 code_generation_target *c_target = NULL;
 void CTarget::create_target(void) {
     c_target = CodeGen::Targets::new(I"c");
 
-    METHOD_ADD(c_target, BEGIN_GENERATION_MTID, CTarget::begin_generation);
-    METHOD_ADD(c_target, END_GENERATION_MTID, CTarget::end_generation);
+    METHOD_ADD(c_target, BEGIN_GENERATION_MTID, CTarget::begin_generation);
+    METHOD_ADD(c_target, END_GENERATION_MTID, CTarget::end_generation);
 
+    CMemoryModel::initialise(c_target);
     CObjectModel::initialise(c_target);
 
     METHOD_ADD(c_target, GENERAL_SEGMENT_MTID, CTarget::general_segment);
@@ -114,110 +115,129 @@ function togglePopup(material_id) {
     METHOD_ADD(c_target, BEGIN_OPCODE_MTID, CTarget::begin_opcode);
     METHOD_ADD(c_target, SUPPLY_OPERAND_MTID, CTarget::supply_operand);
     METHOD_ADD(c_target, END_OPCODE_MTID, CTarget::end_opcode);
-    METHOD_ADD(c_target, BEGIN_ARRAY_MTID, CTarget::begin_array);
-    METHOD_ADD(c_target, ARRAY_ENTRY_MTID, CTarget::array_entry);
-    METHOD_ADD(c_target, END_ARRAY_MTID, CTarget::end_array);
     METHOD_ADD(c_target, OFFER_PRAGMA_MTID, CTarget::offer_pragma)
     METHOD_ADD(c_target, NEW_FAKE_ACTION_MTID, CTarget::new_fake_action);
 }
 
-

§2. Segmentation.

+

§2. Static supporting code. The C code generated here would not compile as a stand-alone file. It needs +to use variables and functions from a small unchanging library called +inform7_clib.h. (The .h there is questionable, since this is not purely +a header file: it contains actual content and not only predeclarations. On +the other hand, it serves the same basic purpose.) +

-
enum c_fundamental_types_I7CGS
-enum c_ids_and_maxima_I7CGS
-enum c_header_matter_I7CGS
-enum c_predeclarations_I7CGS
-enum c_very_early_matter_I7CGS
-enum c_constants_1_I7CGS
-enum c_constants_2_I7CGS
-enum c_constants_3_I7CGS
-enum c_constants_4_I7CGS
-enum c_constants_5_I7CGS
-enum c_constants_6_I7CGS
-enum c_constants_7_I7CGS
-enum c_constants_8_I7CGS
-enum c_constants_9_I7CGS
-enum c_constants_10_I7CGS
-enum c_early_matter_I7CGS
-enum c_text_literals_code_I7CGS
-enum c_summations_at_eof_I7CGS
-enum c_arrays_at_eof_I7CGS
-enum c_globals_array_I7CGS
-enum c_main_matter_I7CGS
-enum c_functions_at_eof_I7CGS
-enum c_code_at_eof_I7CGS
-enum c_verbs_at_eof_I7CGS
-enum c_stubs_at_eof_I7CGS
-enum c_property_offset_creator_I7CGS
-enum c_mem_I7CGS
-enum c_initialiser_I7CGS
-
-
-int C_target_segments[] = {
-    c_fundamental_types_I7CGS,
-    c_ids_and_maxima_I7CGS,
-    c_header_matter_I7CGS,
-    c_predeclarations_I7CGS,
-    c_very_early_matter_I7CGS,
-    c_constants_1_I7CGS,
-    c_constants_2_I7CGS,
-    c_constants_3_I7CGS,
-    c_constants_4_I7CGS,
-    c_constants_5_I7CGS,
-    c_constants_6_I7CGS,
-    c_constants_7_I7CGS,
-    c_constants_8_I7CGS,
-    c_constants_9_I7CGS,
-    c_constants_10_I7CGS,
-    c_early_matter_I7CGS,
-    c_text_literals_code_I7CGS,
-    c_summations_at_eof_I7CGS,
-    c_arrays_at_eof_I7CGS,
-    c_globals_array_I7CGS,
-    c_main_matter_I7CGS,
-    c_functions_at_eof_I7CGS,
-    c_code_at_eof_I7CGS,
-    c_verbs_at_eof_I7CGS,
-    c_stubs_at_eof_I7CGS,
-    c_property_offset_creator_I7CGS,
-    c_mem_I7CGS,
-    c_initialiser_I7CGS,
-    -1
-};
-
-

§3. State data.

+

The code we generate here can only make sense if read alongside inform7_clib.h, +and vice versa, so the file is presented here in installments. This is the +first of those: +

-
define C_GEN_DATA(x) ((C_generation_data *) (gen->target_specific_data))->x
+
+/* This is a library of C code to support Inform or other Inter programs compiled
+   tp ANSI C. It was generated mechanically from the Inter source code, so to
+   change it, edit that and not this. */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+
  • This is part of the extract file inform7_clib.h.
+

§3. Segmentation.

+ +
enum c_fundamental_types_I7CGS
+enum c_ids_and_maxima_I7CGS
+enum c_header_matter_I7CGS
+enum c_predeclarations_I7CGS
+enum c_very_early_matter_I7CGS
+enum c_constants_1_I7CGS
+enum c_constants_2_I7CGS
+enum c_constants_3_I7CGS
+enum c_constants_4_I7CGS
+enum c_constants_5_I7CGS
+enum c_constants_6_I7CGS
+enum c_constants_7_I7CGS
+enum c_constants_8_I7CGS
+enum c_constants_9_I7CGS
+enum c_constants_10_I7CGS
+enum c_early_matter_I7CGS
+enum c_text_literals_code_I7CGS
+enum c_summations_at_eof_I7CGS
+enum c_arrays_at_eof_I7CGS
+enum c_globals_array_I7CGS
+enum c_main_matter_I7CGS
+enum c_functions_at_eof_I7CGS
+enum c_code_at_eof_I7CGS
+enum c_verbs_at_eof_I7CGS
+enum c_stubs_at_eof_I7CGS
+enum c_property_offset_creator_I7CGS
+enum c_mem_I7CGS
+enum c_initialiser_I7CGS
+
+
+int C_target_segments[] = {
+    c_fundamental_types_I7CGS,
+    c_ids_and_maxima_I7CGS,
+    c_header_matter_I7CGS,
+    c_predeclarations_I7CGS,
+    c_very_early_matter_I7CGS,
+    c_constants_1_I7CGS,
+    c_constants_2_I7CGS,
+    c_constants_3_I7CGS,
+    c_constants_4_I7CGS,
+    c_constants_5_I7CGS,
+    c_constants_6_I7CGS,
+    c_constants_7_I7CGS,
+    c_constants_8_I7CGS,
+    c_constants_9_I7CGS,
+    c_constants_10_I7CGS,
+    c_early_matter_I7CGS,
+    c_text_literals_code_I7CGS,
+    c_summations_at_eof_I7CGS,
+    c_arrays_at_eof_I7CGS,
+    c_globals_array_I7CGS,
+    c_main_matter_I7CGS,
+    c_functions_at_eof_I7CGS,
+    c_code_at_eof_I7CGS,
+    c_verbs_at_eof_I7CGS,
+    c_stubs_at_eof_I7CGS,
+    c_property_offset_creator_I7CGS,
+    c_mem_I7CGS,
+    c_initialiser_I7CGS,
+    -1
+};
+
+
  • This is part of the extract file inform7_clib.h.
+

§4. State data.

+ +
define C_GEN_DATA(x) ((C_generation_data *) (gen->target_specific_data))->x
 
 typedef struct C_generation_data {
     text_stream *double_quoted_C;
     int no_double_quoted_C_strings;
-    int extent_of_i7mem;
     int C_dword_count;
     int C_action_count;
     struct dictionary *C_vm_dictionary;
+    struct C_generation_memory_model_data memdata;
     struct C_generation_object_model_data objdata;
     CLASS_DEFINITION
 } C_generation_data;
 
-void CTarget::initialise_data(code_generation *gen) {
+void CTarget::initialise_data(code_generation *gen) {
+    CMemoryModel::initialise_data(gen);
     CObjectModel::initialise_data(gen);
     C_GEN_DATA(double_quoted_C) = Str::new();
     C_GEN_DATA(no_double_quoted_C_strings) = 0;
-    C_GEN_DATA(extent_of_i7mem) = 0;
     C_GEN_DATA(C_dword_count) = 0;
     C_GEN_DATA(C_action_count) = 0;
     C_GEN_DATA(C_vm_dictionary) = Dictionaries::new(1024, TRUE);
 }
 
  • The structure C_generation_data is private to this section.
-

§4. Begin and end.

+

§5. Begin and end.

 int CTarget::begin_generation(code_generation_target *cgt, code_generation *gen) {
     CodeGen::create_segments(gen, CREATE(C_generation_data), C_target_segments);
-    CTarget::initialise_data(gen);
+    CTarget::initialise_data(gen);
 
     CTarget::fix_locals(gen);
 
@@ -232,7 +252,7 @@ function togglePopup(material_id) {
     WRITE("#include \"inform7_clib.h\"\n");
     CodeGen::deselect(gen, saved);
 
-    CTarget::begin_memory(gen);
+    CMemoryModel::begin(gen);
     CTarget::begin_functions(gen);
     CObjectModel::begin(gen);
     CTarget::begin_dictionary_words(gen);
@@ -241,7 +261,7 @@ function togglePopup(material_id) {
 }
 
 int CTarget::end_generation(code_generation_target *cgt, code_generation *gen) {
-    CTarget::end_memory(gen);
+    CMemoryModel::end(gen);
     CTarget::end_functions(gen);
     CObjectModel::end(gen);
     CTarget::end_dictionary_words(gen);
@@ -250,7 +270,7 @@ function togglePopup(material_id) {
 }
 
diff --git a/docs/final-module/index.html b/docs/final-module/index.html index bfeca4efc..2a938d50b 100644 --- a/docs/final-module/index.html +++ b/docs/final-module/index.html @@ -151,6 +151,16 @@ Generating C - To generate I6 code from intermediate code.

+
  • +

    + C References - + How changes to storage objects are translated into C.

    +
  • +
  • +

    + C Memory Model - + How arrays of all kinds are stored in C.

    +
  • C Implementation - diff --git a/inform7/Figures/memory-diagnostics.txt b/inform7/Figures/memory-diagnostics.txt index aceb6fc5a..653fd223f 100644 --- a/inform7/Figures/memory-diagnostics.txt +++ b/inform7/Figures/memory-diagnostics.txt @@ -1,9 +1,9 @@ -Total memory consumption was 399081K = 390 MB +Total memory consumption was 399082K = 390 MB -60.3% was used for 1995521 objects, in 371145 frames in 301 x 800K = 240800K = 235 MB: +60.3% was used for 1995621 objects, in 371146 frames in 301 x 800K = 240800K = 235 MB: 10.2% inter_tree_node_array 58 x 8192 = 475136 objects, 41813824 bytes - 7.0% text_stream_array 5128 x 100 = 512800 objects, 28880896 bytes + 7.0% text_stream_array 5129 x 100 = 512900 objects, 28886528 bytes 4.2% linked_list 30848 objects, 17274880 bytes 3.9% inter_symbol_array 139 x 1024 = 142336 objects, 15946080 bytes 2.5% parse_node 129399 objects, 10351920 bytes @@ -238,7 +238,7 @@ Total memory consumption was 399081K = 390 MB 39.6% was used for memory not allocated for objects: - 21.1% text stream storage 86232716 bytes in 530547 claims + 21.1% text stream storage 86234244 bytes in 530559 claims 4.4% dictionary storage 18153984 bytes in 33230 claims ---- sorting 744 bytes in 3 claims 1.7% source text 7200000 bytes in 3 claims @@ -256,5 +256,5 @@ Total memory consumption was 399081K = 390 MB ---- code generation workspace for objects 9648 bytes in 9 claims ---- emitter array storage 161920 bytes in 2064 claims -18.7% was overhead - 76757816 bytes = 74958K = 73 MB +18.7% was overhead - 76752184 bytes = 74953K = 73 MB diff --git a/inform7/Figures/timings-diagnostics.txt b/inform7/Figures/timings-diagnostics.txt index 656b70a6e..e7e78b5de 100644 --- a/inform7/Figures/timings-diagnostics.txt +++ b/inform7/Figures/timings-diagnostics.txt @@ -1,6 +1,6 @@ 100.0% in inform7 run - 55.1% in compilation to Inter - 40.1% in //Sequence::undertake_queued_tasks// + 55.3% in compilation to Inter + 40.2% in //Sequence::undertake_queued_tasks// 3.5% in //MajorNodes::pre_pass// 2.5% in //MajorNodes::pass_1// 1.5% in //RTPhrasebook::compile_entries// @@ -16,11 +16,11 @@ 0.1% in //RTKindConstructors::compile_permissions// 0.1% in //Task::make_built_in_kind_constructors// 0.1% in //World::stages_II_and_III// - 1.8% not specifically accounted for - 42.8% in running Inter pipeline - 10.7% in step preparation - 9.7% in inter step 7/14: consolidate-text - 8.3% in inter step 14/14: generate inform6 -> auto.inf + 1.9% not specifically accounted for + 42.6% in running Inter pipeline + 10.9% in step preparation + 9.8% in inter step 7/14: consolidate-text + 8.2% in inter step 14/14: generate inform6 -> auto.inf 7.9% in inter step 2/14: link 1.5% in inter step 10/14: make-identifiers-unique 0.4% in inter step 11/14: reconcile-verbs @@ -31,6 +31,6 @@ 0.1% in inter step 12/14: eliminate-redundant-labels 0.1% in inter step 4/14: parse-linked-matter 0.1% in inter step 5/14: resolve-conditional-compilation - 2.4% not specifically accounted for + 2.1% not specifically accounted for 1.7% in supervisor 0.3% not specifically accounted for diff --git a/inform7/Internal/Miscellany/inform7_clib.h b/inform7/Internal/Miscellany/inform7_clib.h index 24527dfa6..4beb88f04 100644 --- a/inform7/Internal/Miscellany/inform7_clib.h +++ b/inform7/Internal/Miscellany/inform7_clib.h @@ -1,9 +1,57 @@ +/* This is a library of C code to support Inform or other Inter programs compiled + tp ANSI C. It was generated mechanically from the Inter source code, so to + change it, edit that and not this. */ + #include #include -typedef struct i7varargs { - i7val args[10]; -} i7varargs; +#define i7_lvalue_SET 1 +#define i7_lvalue_PREDEC 2 +#define i7_lvalue_POSTDEC 3 +#define i7_lvalue_PREINC 4 +#define i7_lvalue_POSTINC 5 +#define i7_lvalue_SETBIT 6 +#define i7_lvalue_CLEARBIT 7 +i7byte i7mem[]; +i7val i7_read_word(i7byte data[], i7val array_address, i7val array_index) { + int byte_position = array_address + 4*array_index; + return (i7val) data[byte_position] + + 0x100*((i7val) data[byte_position + 1]) + + 0x10000*((i7val) data[byte_position + 2]) + + 0x1000000*((i7val) data[byte_position + 3]); +} +#define i7_lvalue_SET 1 +#define i7_lvalue_PREDEC 2 +#define i7_lvalue_POSTDEC 3 +#define i7_lvalue_PREINC 4 +#define i7_lvalue_POSTINC 5 +#define i7_lvalue_SETBIT 6 +#define i7_lvalue_CLEARBIT 7 +#define I7BYTE_3(V) ((V & 0xFF000000) >> 24) +#define I7BYTE_2(V) ((V & 0x00FF0000) >> 16) +#define I7BYTE_1(V) ((V & 0x0000FF00) >> 8) +#define I7BYTE_0(V) (V & 0x000000FF) + +i7val i7_write_word(i7byte data[], i7val array_address, i7val array_index, i7val new_val, int way) { + i7val old_val = i7_read_word(data, array_address, array_index); + i7val return_val = new_val; + switch (way) { + case i7_lvalue_PREDEC: return_val = old_val; new_val = old_val-1; break; + case i7_lvalue_POSTDEC: return_val = old_val-1; new_val = old_val-1; break; + case i7_lvalue_PREINC: return_val = old_val; new_val = old_val+1; break; + case i7_lvalue_POSTINC: return_val = old_val+1; new_val = old_val+1; break; + case i7_lvalue_SETBIT: new_val = old_val | new_val; return_val = new_val; break; + case i7_lvalue_CLEARBIT: new_val = old_val &(~new_val); return_val = new_val; break; + } + int byte_position = array_address + 4*array_index; + data[byte_position] = I7BYTE_0(new_val); + data[byte_position+1] = I7BYTE_1(new_val); + data[byte_position+2] = I7BYTE_2(new_val); + data[byte_position+3] = I7BYTE_3(new_val); + return return_val; +} +#include +#include i7val i7_mgl_self = 0; i7val i7_mgl_sp = 0; @@ -13,39 +61,6 @@ i7val i7_mgl_debug_flag = 0; i7val i7_tmp = 0; int i7_seed = 197; -#define i7_cpv_SET 1 -#define i7_cpv_PREDEC 2 -#define i7_cpv_POSTDEC 3 -#define i7_cpv_PREINC 4 -#define i7_cpv_POSTINC 5 - -#define I7BYTE_3(V) ((V & 0xFF000000) >> 24) -#define I7BYTE_2(V) ((V & 0x00FF0000) >> 16) -#define I7BYTE_1(V) ((V & 0x0000FF00) >> 8) -#define I7BYTE_0(V) (V & 0x000000FF) - -i7val i7_lookup(i7byte i7bytes[], i7val offset, i7val ind) { - ind = offset + 4*ind; - return ((i7val) i7bytes[ind]) + 0x100*((i7val) i7bytes[ind+1]) + - 0x10000*((i7val) i7bytes[ind+2]) + 0x1000000*((i7val) i7bytes[ind+3]); -} - -i7val write_i7_lookup(i7byte i7bytes[], i7val offset, i7val ind, i7val V, int way) { - i7val val = i7_lookup(i7bytes, offset, ind); - i7val RV = V; - switch (way) { - case i7_cpv_PREDEC: RV = val; V = val-1; break; - case i7_cpv_POSTDEC: RV = val-1; V = val-1; break; - case i7_cpv_PREINC: RV = val; V = val+1; break; - case i7_cpv_POSTINC: RV = val+1; V = val+1; break; - } - ind = offset + 4*ind; - i7bytes[ind] = I7BYTE_0(V); - i7bytes[ind+1] = I7BYTE_1(V); - i7bytes[ind+2] = I7BYTE_2(V); - i7bytes[ind+3] = I7BYTE_3(V); - return RV; -} void glulx_accelfunc(i7val x, i7val y) { printf("Unimplemented: glulx_accelfunc.\n"); @@ -487,6 +502,11 @@ i7val i7_mgl_sharp_dictionary_table = 0; i7val i7_mgl_sharp_grammar_table = 0; #define i7_mgl_FLOAT_NAN 0 + +typedef struct i7varargs { + i7val args[10]; +} i7varargs; + i7val fn_i7_mgl_metaclass(int n, i7val id) { if (id <= 0) return 0; if (id >= I7VAL_FUNCTIONS_BASE) return i7_mgl_Routine; @@ -520,14 +540,6 @@ typedef struct i7_property_set { } i7_property_set; i7_property_set i7_properties[i7_max_objects]; -i7val i7_read_prop_value(i7val owner_id, i7val prop_id) { - if ((owner_id <= 0) || (owner_id >= i7_max_objects) || - (prop_id < 0) || (prop_id >= i7_no_property_ids)) return 0; - while (i7_properties[(int) owner_id].value_set[(int) prop_id] == 0) - owner_id = i7_class_of[owner_id]; - return i7_properties[(int) owner_id].value[(int) prop_id]; -} - void i7_write_prop_value(i7val owner_id, i7val prop_id, i7val val) { if ((owner_id <= 0) || (owner_id >= i7_max_objects) || (prop_id < 0) || (prop_id >= i7_no_property_ids)) { @@ -537,14 +549,24 @@ void i7_write_prop_value(i7val owner_id, i7val prop_id, i7val val) { i7_properties[(int) owner_id].value[(int) prop_id] = val; i7_properties[(int) owner_id].value_set[(int) prop_id] = 1; } +i7val i7_read_prop_value(i7val owner_id, i7val prop_id) { + if ((owner_id <= 0) || (owner_id >= i7_max_objects) || + (prop_id < 0) || (prop_id >= i7_no_property_ids)) return 0; + while (i7_properties[(int) owner_id].value_set[(int) prop_id] == 0) + owner_id = i7_class_of[owner_id]; + return i7_properties[(int) owner_id].value[(int) prop_id]; +} + i7val i7_change_prop_value(i7val obj, i7val pr, i7val to, int way) { i7val val = i7_read_prop_value(obj, pr), new_val = val; switch (way) { - case i7_cpv_SET: i7_write_prop_value(obj, pr, to); new_val = to; break; - case i7_cpv_PREDEC: new_val = val; i7_write_prop_value(obj, pr, val-1); break; - case i7_cpv_POSTDEC: new_val = val-1; i7_write_prop_value(obj, pr, new_val); break; - case i7_cpv_PREINC: new_val = val; i7_write_prop_value(obj, pr, val+1); break; - case i7_cpv_POSTINC: new_val = val+1; i7_write_prop_value(obj, pr, new_val); break; + case i7_lvalue_SET: i7_write_prop_value(obj, pr, to); new_val = to; break; + case i7_lvalue_PREDEC: new_val = val; i7_write_prop_value(obj, pr, val-1); break; + case i7_lvalue_POSTDEC: new_val = val-1; i7_write_prop_value(obj, pr, new_val); break; + case i7_lvalue_PREINC: new_val = val; i7_write_prop_value(obj, pr, val+1); break; + case i7_lvalue_POSTINC: new_val = val+1; i7_write_prop_value(obj, pr, new_val); break; + case i7_lvalue_SETBIT: new_val = val | new_val; i7_write_prop_value(obj, pr, new_val); break; + case i7_lvalue_CLEARBIT: new_val = val &(~new_val); i7_write_prop_value(obj, pr, new_val); break; } return new_val; } diff --git a/inter/final-module/Chapter 2/Constants and Literals.w b/inter/final-module/Chapter 2/Constants and Literals.w index 068ae7e1a..ad57d3793 100644 --- a/inter/final-module/Chapter 2/Constants and Literals.w +++ b/inter/final-module/Chapter 2/Constants and Literals.w @@ -385,3 +385,37 @@ text_stream *CodeGen::CL::name(inter_symbol *symb) { if (Inter::Symbols::get_translate(symb)) return Inter::Symbols::get_translate(symb); return symb->symbol_name; } + +@ = +int CodeGen::CL::node_is_word_array_ref(inter_tree *I, inter_tree_node *P) { + int reffed = FALSE; + while (P->W.data[ID_IFLD] == REFERENCE_IST) { + P = InterTree::first_child(P); + reffed = TRUE; + } + if (P->W.data[ID_IFLD] == INV_IST) { + if (P->W.data[METHOD_INV_IFLD] == INVOKED_PRIMITIVE) { + inter_symbol *prim = Inter::Inv::invokee(P); + inter_ti bip = Primitives::to_bip(I, prim); + if (bip == LOOKUPREF_BIP) return TRUE; + if ((bip == LOOKUP_BIP) && (reffed)) return TRUE; + } + } + return FALSE; +} + +int CodeGen::CL::node_is_property_ref(inter_tree *I, inter_tree_node *P) { + int reffed = FALSE; + while (P->W.data[ID_IFLD] == REFERENCE_IST) { + P = InterTree::first_child(P); + reffed = TRUE; + } + if (P->W.data[ID_IFLD] == INV_IST) { + if (P->W.data[METHOD_INV_IFLD] == INVOKED_PRIMITIVE) { + inter_symbol *prim = Inter::Inv::invokee(P); + inter_ti bip = Primitives::to_bip(I, prim); + if (bip == PROPERTYVALUE_BIP) return TRUE; + } + } + return FALSE; +} diff --git a/inter/final-module/Chapter 5/C Implementation.w b/inter/final-module/Chapter 5/C Implementation.w index 690ddaef7..0c7b1de2d 100644 --- a/inter/final-module/Chapter 5/C Implementation.w +++ b/inter/final-module/Chapter 5/C Implementation.w @@ -8,10 +8,6 @@ To generate I6 code from intermediate code. #include #include -typedef struct i7varargs { - i7val args[10]; -} i7varargs; - i7val i7_mgl_self = 0; i7val i7_mgl_sp = 0; #define i7_mgl_Grammar__Version 2 @@ -20,39 +16,6 @@ i7val i7_mgl_debug_flag = 0; i7val i7_tmp = 0; int i7_seed = 197; -#define i7_cpv_SET 1 -#define i7_cpv_PREDEC 2 -#define i7_cpv_POSTDEC 3 -#define i7_cpv_PREINC 4 -#define i7_cpv_POSTINC 5 - -#define I7BYTE_3(V) ((V & 0xFF000000) >> 24) -#define I7BYTE_2(V) ((V & 0x00FF0000) >> 16) -#define I7BYTE_1(V) ((V & 0x0000FF00) >> 8) -#define I7BYTE_0(V) (V & 0x000000FF) - -i7val i7_lookup(i7byte i7bytes[], i7val offset, i7val ind) { - ind = offset + 4*ind; - return ((i7val) i7bytes[ind]) + 0x100*((i7val) i7bytes[ind+1]) + - 0x10000*((i7val) i7bytes[ind+2]) + 0x1000000*((i7val) i7bytes[ind+3]); -} - -i7val write_i7_lookup(i7byte i7bytes[], i7val offset, i7val ind, i7val V, int way) { - i7val val = i7_lookup(i7bytes, offset, ind); - i7val RV = V; - switch (way) { - case i7_cpv_PREDEC: RV = val; V = val-1; break; - case i7_cpv_POSTDEC: RV = val-1; V = val-1; break; - case i7_cpv_PREINC: RV = val; V = val+1; break; - case i7_cpv_POSTINC: RV = val+1; V = val+1; break; - } - ind = offset + 4*ind; - i7bytes[ind] = I7BYTE_0(V); - i7bytes[ind+1] = I7BYTE_1(V); - i7bytes[ind+2] = I7BYTE_2(V); - i7bytes[ind+3] = I7BYTE_3(V); - return RV; -} void glulx_accelfunc(i7val x, i7val y) { printf("Unimplemented: glulx_accelfunc.\n"); @@ -494,36 +457,16 @@ i7val i7_mgl_sharp_dictionary_table = 0; i7val i7_mgl_sharp_grammar_table = 0; #define i7_mgl_FLOAT_NAN 0 + +typedef struct i7varargs { + i7val args[10]; +} i7varargs; + = @ = - -void CTarget::begin_memory(code_generation *gen) { - generated_segment *saved = CodeGen::select(gen, c_header_matter_I7CGS); - text_stream *OUT = CodeGen::current(gen); - WRITE("i7byte i7mem[];\n"); - CodeGen::deselect(gen, saved); - - saved = CodeGen::select(gen, c_mem_I7CGS); - OUT = CodeGen::current(gen); - WRITE("i7byte i7mem[] = {\n"); - CodeGen::deselect(gen, saved); -} - -void CTarget::end_memory(code_generation *gen) { - generated_segment *saved = CodeGen::select(gen, c_mem_I7CGS); - text_stream *OUT = CodeGen::current(gen); - WRITE("0, 0 };\n"); - CodeGen::deselect(gen, saved); - - saved = CodeGen::select(gen, c_predeclarations_I7CGS); - OUT = CodeGen::current(gen); - WRITE("char *dqs[] = {\n%S\"\" };\n", C_GEN_DATA(double_quoted_C)); - CodeGen::deselect(gen, saved); -} - void CTarget::begin_dictionary_words(code_generation *gen) { } @@ -535,6 +478,11 @@ void CTarget::end_dictionary_words(code_generation *gen) { WRITE("#define i7_p_dword_%d %d\n", i, 2*i + 1); } CodeGen::deselect(gen, saved); + + saved = CodeGen::select(gen, c_predeclarations_I7CGS); + OUT = CodeGen::current(gen); + WRITE("char *dqs[] = {\n%S\"\" };\n", C_GEN_DATA(double_quoted_C)); + CodeGen::deselect(gen, saved); } void CTarget::end_functions(code_generation *gen) { @@ -622,14 +570,20 @@ void CTarget::mangle(code_generation_target *cgt, OUTPUT_STREAM, text_stream *id } else WRITE("i7_mgl_%S", identifier); } -int C_write_lookup_mode = FALSE; - int CTarget::compile_primitive(code_generation_target *cgt, code_generation *gen, inter_symbol *prim_name, inter_tree_node *P) { text_stream *OUT = CodeGen::current(gen); int suppress_terminal_semicolon = FALSE; inter_tree *I = gen->from; inter_ti bip = Primitives::to_bip(I, prim_name); + + int r = CReferences::compile_primitive(gen, bip, P); + if (r != NOT_APPLICABLE) return r; + r = CMemoryModel::compile_primitive(gen, bip, P); + if (r != NOT_APPLICABLE) return r; + r = CObjectModel::compile_primitive(gen, bip, P); + if (r != NOT_APPLICABLE) return r; + switch (bip) { case INVERSION_BIP: break; /* we won't support this in C */ @@ -662,31 +616,6 @@ int CTarget::compile_primitive(code_generation_target *cgt, code_generation *gen case PUSH_BIP: WRITE("i7_push("); INV_A1; WRITE(")"); break; case PULL_BIP: INV_A1; WRITE(" = i7_pull()"); break; - case PREINCREMENT_BIP: @; break; - case POSTINCREMENT_BIP: @; break; - case PREDECREMENT_BIP: @; break; - case POSTDECREMENT_BIP: @; break; - case STORE_BIP: @; break; - case SETBIT_BIP: INV_A1; WRITE(" = "); INV_A1; WRITE(" | "); INV_A2; break; - case CLEARBIT_BIP: INV_A1; WRITE(" = "); INV_A1; WRITE(" &~ ("); INV_A2; WRITE(")"); break; - case LOOKUP_BIP: if (C_write_lookup_mode) { - C_write_lookup_mode = FALSE; - @; - } else { - @; - } - break; - case LOOKUPBYTE_BIP: @; break; - case LOOKUPREF_BIP: @; break; - case PROPERTYADDRESS_BIP: WRITE("i7_prop_addr("); INV_A1; WRITE(", "); INV_A2; WRITE(")"); break; - case PROPERTYLENGTH_BIP: WRITE("i7_prop_len("); INV_A1; WRITE(", "); INV_A2; WRITE(")"); break; - case PROPERTYVALUE_BIP: if (C_write_lookup_mode) { - C_write_lookup_mode = FALSE; - WRITE("i7_change_prop_value("); INV_A1; WRITE(", "); INV_A2; WRITE(", "); - } else { - WRITE("i7_read_prop_value("); INV_A1; WRITE(", "); INV_A2; WRITE(")"); - } - break; case BREAK_BIP: WRITE("break"); break; case CONTINUE_BIP: WRITE("continue"); break; case RETURN_BIP: @; break; @@ -713,13 +642,6 @@ int CTarget::compile_primitive(code_generation_target *cgt, code_generation *gen WRITE("i7_call_5("); INV_A1; WRITE(", "); INV_A2; WRITE(", "); INV_A3; WRITE(", "); INV_A4; WRITE(", "); INV_A5; WRITE(", "); INV_A6; WRITE(")"); break; - case MESSAGE0_BIP: WRITE("i7_call_0(i7_read_prop_value("); INV_A1; WRITE(", "); INV_A2; WRITE("))"); break; - case MESSAGE1_BIP: WRITE("i7_call_1(i7_read_prop_value("); INV_A1; WRITE(", "); INV_A2; WRITE("), "); - INV_A3; WRITE(")"); break; - case MESSAGE2_BIP: WRITE("i7_call_2(i7_read_prop_value("); INV_A1; WRITE(", "); INV_A2; WRITE("), "); - INV_A3; WRITE(", "); INV_A4; WRITE(")"); break; - case MESSAGE3_BIP: WRITE("i7_call_3(i7_read_prop_value("); INV_A1; WRITE(", "); INV_A2; WRITE("), "); - INV_A3; WRITE(", "); INV_A4; WRITE(", "); INV_A5; WRITE(")"); break; case CALLMESSAGE0_BIP: WRITE("i7_ccall_0("); INV_A1; WRITE(")"); break; case CALLMESSAGE1_BIP: WRITE("i7_ccall_1("); INV_A1; WRITE(", "); INV_A2; WRITE(")"); break; @@ -740,8 +662,6 @@ int CTarget::compile_primitive(code_generation_target *cgt, code_generation *gen case MOVE_BIP: WRITE("i7_move("); INV_A1; WRITE(", "); INV_A2; WRITE(")"); break; case REMOVE_BIP: WRITE("i7_move("); INV_A1; WRITE(", 0)"); break; - case GIVE_BIP: WRITE("i7_give("); INV_A1; WRITE(", "); INV_A2; WRITE(", 1)"); break; - case TAKE_BIP: WRITE("i7_give("); INV_A1; WRITE(", "); INV_A2; WRITE(", 0)"); break; case ALTERNATIVECASE_BIP: INV_A1; WRITE(", "); INV_A2; break; case SEQUENTIAL_BIP: WRITE("("); INV_A1; WRITE(","); INV_A2; WRITE(")"); break; @@ -789,43 +709,6 @@ int CTarget::compile_primitive(code_generation_target *cgt, code_generation *gen @ = CTarget::comparison(cgt, gen, bip, InterTree::first_child(P), InterTree::second_child(P)); -@ = - text_stream *store_form = NULL; - switch (bip) { - case PREINCREMENT_BIP: store_form = I"i7_cpv_PREINC"; break; - case POSTINCREMENT_BIP: store_form = I"i7_cpv_POSTINC"; break; - case PREDECREMENT_BIP: store_form = I"i7_cpv_PREDEC"; break; - case POSTDECREMENT_BIP: store_form = I"i7_cpv_POSTDEC"; break; - case STORE_BIP: store_form = I"i7_cpv_SET"; break; - } - inter_tree_node *ref = InterTree::first_child(P); - if (CTarget::basically_an_array_write(gen->from, ref)) { - WRITE("("); C_write_lookup_mode = TRUE; INV_A1; C_write_lookup_mode = FALSE; - if (bip == STORE_BIP) { INV_A2; } else { WRITE("0"); } - WRITE(", %S))", store_form); - } else if (CTarget::basically_a_property_write(gen->from, ref)) { - WRITE("("); C_write_lookup_mode = TRUE; INV_A1; C_write_lookup_mode = FALSE; - if (bip == STORE_BIP) { INV_A2; } else { WRITE("0"); } - WRITE(", %S))", store_form); - } else { - switch (bip) { - case PREINCREMENT_BIP: WRITE("++("); INV_A1; WRITE(")"); break; - case POSTINCREMENT_BIP: WRITE("("); INV_A1; WRITE(")++"); break; - case PREDECREMENT_BIP: WRITE("--("); INV_A1; WRITE(")"); break; - case POSTDECREMENT_BIP: WRITE("("); INV_A1; WRITE(")--"); break; - case STORE_BIP: WRITE("("); INV_A1; WRITE(" = "); INV_A2; WRITE(")"); break; - } - } - -@ = - WRITE("i7_lookup(i7mem, "); INV_A1; WRITE(", "); INV_A2; WRITE(")"); - -@ = - WRITE("write_i7_lookup(i7mem, "); INV_A1; WRITE(", "); INV_A2; WRITE(", "); - -@ = - WRITE("i7mem["); INV_A1; WRITE(" + "); INV_A2; WRITE("]"); - @ = int rboolean = NOT_APPLICABLE; inter_tree_node *V = InterTree::first_child(P); @@ -1103,20 +986,20 @@ int CTarget::declare_variable(code_generation_target *cgt, code_generation *gen, CodeGen::deselect(gen, saved); } if (Inter::Symbols::read_annotation(var_name, EXPLICIT_VARIABLE_IANN) != 1) { - if (k == 0) CTarget::begin_array(cgt, gen, I"Global_Vars", WORD_ARRAY_FORMAT); + if (k == 0) CMemoryModel::begin_array(cgt, gen, I"Global_Vars", WORD_ARRAY_FORMAT); TEMPORARY_TEXT(val) CodeGen::select_temporary(gen, val); CodeGen::CL::literal(gen, NULL, Inter::Packages::scope_of(P), P->W.data[VAL1_VAR_IFLD], P->W.data[VAL2_VAR_IFLD], FALSE); CodeGen::deselect_temporary(gen); - CTarget::array_entry(cgt, gen, val, WORD_ARRAY_FORMAT); + CMemoryModel::array_entry(cgt, gen, val, WORD_ARRAY_FORMAT); DISCARD_TEXT(val) k++; if (k == of) { if (k < 2) { - CTarget::array_entry(cgt, gen, I"0", WORD_ARRAY_FORMAT); - CTarget::array_entry(cgt, gen, I"0", WORD_ARRAY_FORMAT); + CMemoryModel::array_entry(cgt, gen, I"0", WORD_ARRAY_FORMAT); + CMemoryModel::array_entry(cgt, gen, I"0", WORD_ARRAY_FORMAT); } - CTarget::end_array(cgt, gen, WORD_ARRAY_FORMAT); + CMemoryModel::end_array(cgt, gen, WORD_ARRAY_FORMAT); } } return k; @@ -1406,85 +1289,6 @@ void CTarget::declare_local_variable(code_generation_target *cgt, int pass, DISCARD_TEXT(name) } -int C_array_entry_count = 0; -text_stream *C_array_name = NULL; - -void CTarget::begin_array(code_generation_target *cgt, code_generation *gen, text_stream *array_name, int format) { - generated_segment *saved = CodeGen::select(gen, c_predeclarations_I7CGS); - text_stream *OUT = CodeGen::current(gen); - WRITE("#define "); - CTarget::mangle(cgt, OUT, array_name); - WRITE(" %d // An array in format %d\n", C_GEN_DATA(extent_of_i7mem), format); - CodeGen::deselect(gen, saved); - - if (C_array_name == NULL) C_array_name = Str::new(); - Str::clear(C_array_name); WRITE_TO(C_array_name, "%S", array_name); - C_array_entry_count = 0; - - if ((format == TABLE_ARRAY_FORMAT) || (format == BUFFER_ARRAY_FORMAT)) { - TEMPORARY_TEXT(extname) - WRITE_TO(extname, "xt_%S", array_name); - CTarget::array_entry(cgt, gen, extname, format); - DISCARD_TEXT(extname) - } -} - -void CTarget::array_entry(code_generation_target *cgt, code_generation *gen, text_stream *entry, int format) { - generated_segment *saved = CodeGen::select(gen, c_mem_I7CGS); - text_stream *OUT = CodeGen::current(gen); - if ((format == TABLE_ARRAY_FORMAT) || (format == WORD_ARRAY_FORMAT)) { - WRITE(" I7BYTE_0(%S), I7BYTE_1(%S), I7BYTE_2(%S), I7BYTE_3(%S),\n", - entry, entry, entry, entry); - C_GEN_DATA(extent_of_i7mem) += 4; - } else { - WRITE(" (i7byte) %S,\n", entry); - C_GEN_DATA(extent_of_i7mem) += 1; - } - CodeGen::deselect(gen, saved); - C_array_entry_count++; -} - -void CTarget::end_array(code_generation_target *cgt, code_generation *gen, int format) { - generated_segment *saved = CodeGen::select(gen, c_predeclarations_I7CGS); - text_stream *OUT = CodeGen::current(gen); - if ((format == TABLE_ARRAY_FORMAT) || (format == BUFFER_ARRAY_FORMAT)) { - WRITE("#define xt_%S %d\n", C_array_name, C_array_entry_count-1); - } - CodeGen::deselect(gen, saved); -} - -int CTarget::basically_an_array_write(inter_tree *I, inter_tree_node *P) { - int reffed = FALSE; - while (P->W.data[ID_IFLD] == REFERENCE_IST) { - P = InterTree::first_child(P); - reffed = TRUE; - } - if (P->W.data[ID_IFLD] == INV_IST) { - if (P->W.data[METHOD_INV_IFLD] == INVOKED_PRIMITIVE) { - inter_symbol *prim = Inter::Inv::invokee(P); - inter_ti bip = Primitives::to_bip(I, prim); - if (bip == LOOKUPREF_BIP) return TRUE; - if ((bip == LOOKUP_BIP) && (reffed)) return TRUE; - } - } - return FALSE; -} - -int CTarget::basically_a_property_write(inter_tree *I, inter_tree_node *P) { - int reffed = FALSE; - while (P->W.data[ID_IFLD] == REFERENCE_IST) { - P = InterTree::first_child(P); - reffed = TRUE; - } - if (P->W.data[ID_IFLD] == INV_IST) { - if (P->W.data[METHOD_INV_IFLD] == INVOKED_PRIMITIVE) { - inter_symbol *prim = Inter::Inv::invokee(P); - inter_ti bip = Primitives::to_bip(I, prim); - if (bip == PROPERTYVALUE_BIP) return TRUE; - } - } - return FALSE; -} void CTarget::new_fake_action(code_generation_target *cgt, code_generation *gen, text_stream *name) { generated_segment *saved = CodeGen::select(gen, c_predeclarations_I7CGS); diff --git a/inter/final-module/Chapter 5/C Memory Model.w b/inter/final-module/Chapter 5/C Memory Model.w new file mode 100644 index 000000000..a0363fc9e --- /dev/null +++ b/inter/final-module/Chapter 5/C Memory Model.w @@ -0,0 +1,279 @@ +[CMemoryModel::] C Memory Model. + +How arrays of all kinds are stored in C. + +@h Setting up the model. + += +void CMemoryModel::initialise(code_generation_target *cgt) { + METHOD_ADD(cgt, BEGIN_ARRAY_MTID, CMemoryModel::begin_array); + METHOD_ADD(cgt, ARRAY_ENTRY_MTID, CMemoryModel::array_entry); + METHOD_ADD(cgt, END_ARRAY_MTID, CMemoryModel::end_array); +} + +typedef struct C_generation_memory_model_data { + int himem; /* high point of memory: 1 more than the largest legal address */ + struct text_stream *array_name; + int entry_count; + int next_node_is_a_ref; +} C_generation_memory_model_data; + +void CMemoryModel::initialise_data(code_generation *gen) { + C_GEN_DATA(memdata.himem) = 0; + C_GEN_DATA(memdata.array_name) = Str::new(); + C_GEN_DATA(memdata.entry_count) = 0; + C_GEN_DATA(memdata.next_node_is_a_ref) = FALSE; +} + +@h Byte-addressable memory. +The Inter semantics require that there be an area of byte-accessible memory: + +(a) Byte-accessible memory must contain all of the arrays. These can but need +not have alignment gaps in between them. (For C, they do not.) +(b) "Addresses" in this memory identify individual byte positions in it. These +can but need not start at 0. (For C, they do.) They must not be too large to +fit into an Inter value. +(c) When an array name is compiled, its runtime value must be its address. +(d) When an Inter value is stored in byte-accessible memory, it occupies either +2 or 4 consecutive bytes, with the little end first. The result is called a +"word". (For C, always 4, which is always |sizeof(i7val)|.) Conversion between +a word stored in memory and an Inter value must be faithful in both directions. +(e) Words can be stored at any byte position, and not only at (say) multiples +of 2 or 4. +(f) Arrays in memory are free to contain a mixture of bytes and words: some do. +(g) Data may be written in byte form and read back in word form, or vice versa. + +We will manage that with a single C array. This is first predeclared here: + += (text to inform7_clib.h) +i7byte i7mem[]; += + +@ Declaring that array is our main task in this section. + += +void CMemoryModel::begin(code_generation *gen) { + generated_segment *saved = CodeGen::select(gen, c_mem_I7CGS); + text_stream *OUT = CodeGen::current(gen); + WRITE("i7byte i7mem[] = {\n"); + CodeGen::deselect(gen, saved); +} + +@ We will end the array with two dummy bytes (which should never be accessed) +just in case, and to ensure that it is never empty, which would be illegal +in C. + += +void CMemoryModel::end(code_generation *gen) { + generated_segment *saved = CodeGen::select(gen, c_mem_I7CGS); + text_stream *OUT = CodeGen::current(gen); + WRITE("0, 0 };\n"); + CodeGen::deselect(gen, saved); +} + +@h Reading and writing memory. +Given the above array, it's easy to read and write bytes: if |a| is the address +then we can simply refer to |i7mem[a]|. Words are more challenging since we +need to pack and unpack them. + +The following function reads a word which is in entry |array_index| (counting +0, 1, 2, ...) in the array which begins at the byte address |array_address| in +the bank of memory |data|. In practice, we will only every use this function +with |data| set to |i7mem|. + +The equivalent for reading a byte entry is |data[array_address + array_index]|. + += (text to inform7_clib.h) +i7val i7_read_word(i7byte data[], i7val array_address, i7val array_index) { + int byte_position = array_address + 4*array_index; + return (i7val) data[byte_position] + + 0x100*((i7val) data[byte_position + 1]) + + 0x10000*((i7val) data[byte_position + 2]) + + 0x1000000*((i7val) data[byte_position + 3]); +} += + +Now for writing. Memory locations (like variables and property values) can not +only be "set", that is, written to, but also pre-decremented, post-decremented, +pre-incremented and post-incremented. This is the equivalent of being able to +apply |++| or |--| either side, in C terms. The following enumerated constants +identify which of these five operations to perform: + += (text to inform7_clib.h) +#define i7_lvalue_SET 1 +#define i7_lvalue_PREDEC 2 +#define i7_lvalue_POSTDEC 3 +#define i7_lvalue_PREINC 4 +#define i7_lvalue_POSTINC 5 +#define i7_lvalue_SETBIT 6 +#define i7_lvalue_CLEARBIT 7 += + +@ Packing, unlike unpacking, is done with macros so that it is possible to +express a packed word in constant context, which we will need later. + += (text to inform7_clib.h) +#define I7BYTE_3(V) ((V & 0xFF000000) >> 24) +#define I7BYTE_2(V) ((V & 0x00FF0000) >> 16) +#define I7BYTE_1(V) ((V & 0x0000FF00) >> 8) +#define I7BYTE_0(V) (V & 0x000000FF) + +i7val i7_write_word(i7byte data[], i7val array_address, i7val array_index, i7val new_val, int way) { + i7val old_val = i7_read_word(data, array_address, array_index); + i7val return_val = new_val; + switch (way) { + case i7_lvalue_PREDEC: return_val = old_val; new_val = old_val-1; break; + case i7_lvalue_POSTDEC: return_val = old_val-1; new_val = old_val-1; break; + case i7_lvalue_PREINC: return_val = old_val; new_val = old_val+1; break; + case i7_lvalue_POSTINC: return_val = old_val+1; new_val = old_val+1; break; + case i7_lvalue_SETBIT: new_val = old_val | new_val; return_val = new_val; break; + case i7_lvalue_CLEARBIT: new_val = old_val &(~new_val); return_val = new_val; break; + } + int byte_position = array_address + 4*array_index; + data[byte_position] = I7BYTE_0(new_val); + data[byte_position+1] = I7BYTE_1(new_val); + data[byte_position+2] = I7BYTE_2(new_val); + data[byte_position+3] = I7BYTE_3(new_val); + return return_val; +} += + +@h Populating memory with arrays. +Inter supports four sorts of arrays, with behaviour as laid out in this 2x2 grid: += (text) + | entries count 0, 1, 2,... | entry 0 is N, then entries count 1, 2, ..., N +-------------+-------------------------------+----------------------------------------------- +byte entries | BYTE_ARRAY_FORMAT | BUFFER_ARRAY_FORMAT +-------------+-------------------------------+----------------------------------------------- +word entries | WORD_ARRAY_FORMAT | TABLE_ARRAY_FORMAT +-------------+-------------------------------+----------------------------------------------- += + += +void CMemoryModel::begin_array(code_generation_target *cgt, code_generation *gen, + text_stream *array_name, int format) { + Str::clear(C_GEN_DATA(memdata.array_name)); + WRITE_TO(C_GEN_DATA(memdata.array_name), "%S", array_name); + C_GEN_DATA(memdata.entry_count) = 0; + + text_stream *format_name = I"unknown"; + @; + @; + if ((format == TABLE_ARRAY_FORMAT) || (format == BUFFER_ARRAY_FORMAT)) + @; +} + +@ = + switch (format) { + case BYTE_ARRAY_FORMAT: format_name = I"byte"; break; + case WORD_ARRAY_FORMAT: format_name = I"word"; break; + case BUFFER_ARRAY_FORMAT: format_name = I"buffer"; break; + case TABLE_ARRAY_FORMAT: format_name = I"table"; break; + } + +@ Crucially, the array names are |#define| constants declared up at the top +of the source code: they are not variables with pointer types, or something +like that. This means they can legally be used as values elsewhere in |i7mem|, +or as initial values of variables, and so on. + +Object, class and function names can also legally appear as array entries, +because they too are defined constants, equal to their IDs: see //C Object Model//. + +@ = + generated_segment *saved = CodeGen::select(gen, c_predeclarations_I7CGS); + text_stream *OUT = CodeGen::current(gen); + WRITE("#define "); + CTarget::mangle(cgt, OUT, array_name); + WRITE(" %d /* = position in i7mem of %S array %S */\n", + C_GEN_DATA(memdata.himem), format_name, array_name); + CodeGen::deselect(gen, saved); + +@ Of course, right now we don't know |N|, the extent of the array. So we will +refer to this with a constant like |xt_myarray|, which we will retrospectively +predefine when the array ends. + +@ = + TEMPORARY_TEXT(extname) + WRITE_TO(extname, "xt_%S", array_name); + CMemoryModel::array_entry(cgt, gen, extname, format); + DISCARD_TEXT(extname) + +@ The call to |CMemoryModel::begin_array| is then followed by a series of calls to: + += +void CMemoryModel::array_entry(code_generation_target *cgt, code_generation *gen, + text_stream *entry, int format) { + generated_segment *saved = CodeGen::select(gen, c_mem_I7CGS); + text_stream *OUT = CodeGen::current(gen); + if ((format == TABLE_ARRAY_FORMAT) || (format == WORD_ARRAY_FORMAT)) + @ + else + @; + CodeGen::deselect(gen, saved); + C_GEN_DATA(memdata.entry_count)++; +} + +@ = + WRITE(" (i7byte) %S,\n", entry); + C_GEN_DATA(memdata.himem) += 1; + +@ Now we see why it was important for |I7BYTE_0| and so on to be macros: they +use only arithmetic operations which can be constant-folded by the C compiler, +and therefore if |X| is a valid constant-context expression in C then so is +|I7BYTE_0(X)|. + +@ = + WRITE(" I7BYTE_0(%S), I7BYTE_1(%S), I7BYTE_2(%S), I7BYTE_3(%S),\n", + entry, entry, entry, entry); + C_GEN_DATA(memdata.himem) += 4; + +@ When all the entries have been placed, the following is called. It does nothing +except to predeclare the extent constant, if one was used. + += +void CMemoryModel::end_array(code_generation_target *cgt, code_generation *gen, int format) { + if ((format == TABLE_ARRAY_FORMAT) || (format == BUFFER_ARRAY_FORMAT)) { + generated_segment *saved = CodeGen::select(gen, c_predeclarations_I7CGS); + text_stream *OUT = CodeGen::current(gen); + WRITE("#define xt_%S %d\n", + C_GEN_DATA(memdata.array_name), C_GEN_DATA(memdata.entry_count)-1); + CodeGen::deselect(gen, saved); + } +} + +@h Primitives for byte and word lookup. +The signatures here are: + += (text) +primitive !lookup val val -> val +primitive !lookupbyte val val -> val +primitive !lookupref val val -> ref += +|!lookupref| is a slightly redundant abbreviation for |reference| with |!lookup| +as a child. At present, there is no equivalent |!lookupbyteref|. + += +int CMemoryModel::compile_primitive(code_generation *gen, inter_ti bip, inter_tree_node *P) { + text_stream *OUT = CodeGen::current(gen); + switch (bip) { + case LOOKUP_BIP: if (CReferences::am_I_a_ref(gen)) { + @; + } else { + @; + } + break; + case LOOKUPBYTE_BIP: @; break; + case LOOKUPREF_BIP: @; break; + default: return NOT_APPLICABLE; + } + return FALSE; +} + +@ = + WRITE("i7_read_word(i7mem, "); INV_A1; WRITE(", "); INV_A2; WRITE(")"); + +@ = + WRITE("i7mem["); INV_A1; WRITE(" + "); INV_A2; WRITE("]"); + +@ = + WRITE("i7_write_word(i7mem, "); INV_A1; WRITE(", "); INV_A2; WRITE(", "); diff --git a/inter/final-module/Chapter 5/C Object Model.w b/inter/final-module/Chapter 5/C Object Model.w index fac5ce273..41ed0b338 100644 --- a/inter/final-module/Chapter 5/C Object Model.w +++ b/inter/final-module/Chapter 5/C Object Model.w @@ -280,12 +280,12 @@ void CObjectModel::property_offset(code_generation_target *cgt, code_generation if (C_GEN_DATA(objdata.C_property_offsets_made)++ == 0) @; - WRITE("write_i7_lookup(i7mem, "); + WRITE("i7_write_word(i7mem, "); if (as_attr) CTarget::mangle(cgt, OUT, I"attributed_property_offsets"); else CTarget::mangle(cgt, OUT, I"valued_property_offsets"); WRITE(", "); CTarget::mangle(cgt, OUT, prop); - WRITE(", %d, i7_cpv_SET);\n", pos); + WRITE(", %d, i7_lvalue_SET);\n", pos); CodeGen::deselect(gen, saved); } @@ -296,9 +296,9 @@ not deriving from an Inform program. @ = WRITE("i7val fn_i7_mgl_CreatePropertyOffsets(int argc) {\n"); INDENT; WRITE("for (int i=0; i= i7_max_objects) || - (prop_id < 0) || (prop_id >= i7_no_property_ids)) return 0; - while (i7_properties[(int) owner_id].value_set[(int) prop_id] == 0) - owner_id = i7_class_of[owner_id]; - return i7_properties[(int) owner_id].value[(int) prop_id]; -} - void i7_write_prop_value(i7val owner_id, i7val prop_id, i7val val) { if ((owner_id <= 0) || (owner_id >= i7_max_objects) || (prop_id < 0) || (prop_id >= i7_no_property_ids)) { @@ -390,17 +415,27 @@ void i7_write_prop_value(i7val owner_id, i7val prop_id, i7val val) { } = -@h Other things to do with properties. +@ And here sre the functions called by the above primitives: = (text to inform7_clib.h) +i7val i7_read_prop_value(i7val owner_id, i7val prop_id) { + if ((owner_id <= 0) || (owner_id >= i7_max_objects) || + (prop_id < 0) || (prop_id >= i7_no_property_ids)) return 0; + while (i7_properties[(int) owner_id].value_set[(int) prop_id] == 0) + owner_id = i7_class_of[owner_id]; + return i7_properties[(int) owner_id].value[(int) prop_id]; +} + i7val i7_change_prop_value(i7val obj, i7val pr, i7val to, int way) { i7val val = i7_read_prop_value(obj, pr), new_val = val; switch (way) { - case i7_cpv_SET: i7_write_prop_value(obj, pr, to); new_val = to; break; - case i7_cpv_PREDEC: new_val = val; i7_write_prop_value(obj, pr, val-1); break; - case i7_cpv_POSTDEC: new_val = val-1; i7_write_prop_value(obj, pr, new_val); break; - case i7_cpv_PREINC: new_val = val; i7_write_prop_value(obj, pr, val+1); break; - case i7_cpv_POSTINC: new_val = val+1; i7_write_prop_value(obj, pr, new_val); break; + case i7_lvalue_SET: i7_write_prop_value(obj, pr, to); new_val = to; break; + case i7_lvalue_PREDEC: new_val = val; i7_write_prop_value(obj, pr, val-1); break; + case i7_lvalue_POSTDEC: new_val = val-1; i7_write_prop_value(obj, pr, new_val); break; + case i7_lvalue_PREINC: new_val = val; i7_write_prop_value(obj, pr, val+1); break; + case i7_lvalue_POSTINC: new_val = val+1; i7_write_prop_value(obj, pr, new_val); break; + case i7_lvalue_SETBIT: new_val = val | new_val; i7_write_prop_value(obj, pr, new_val); break; + case i7_lvalue_CLEARBIT: new_val = val &(~new_val); i7_write_prop_value(obj, pr, new_val); break; } return new_val; } diff --git a/inter/final-module/Chapter 5/C References.w b/inter/final-module/Chapter 5/C References.w new file mode 100644 index 000000000..21ce7d3d7 --- /dev/null +++ b/inter/final-module/Chapter 5/C References.w @@ -0,0 +1,118 @@ +[CReferences::] C References. + +How changes to storage objects are translated into C. + +@ References identify storage objects which are being written to or otherwise +modified, rather than having their current contents read. + +There are seven possible ways to modify something identified by a reference, +and we need constants to identify these ways at runtime: + += (text to inform7_clib.h) +#define i7_lvalue_SET 1 +#define i7_lvalue_PREDEC 2 +#define i7_lvalue_POSTDEC 3 +#define i7_lvalue_PREINC 4 +#define i7_lvalue_POSTINC 5 +#define i7_lvalue_SETBIT 6 +#define i7_lvalue_CLEARBIT 7 += + +@ Those seven ways correspond to seven Inter primitives, with the following +signatures: += (text) +primitive !store ref val -> val +primitive !preincrement ref -> val +primitive !postincrement ref -> val +primitive !predecrement ref -> val +primitive !postdecrement ref -> val +primitive !setbit ref val -> void +primitive !clearbit ref val -> void += +Since C functions can have their return values freely ignored, we will in fact +implement |!setbit| and |!clearbit| as if they too had the signature +|ref val -> val|. + +For all these primitives, then, the first operand A1 is a |ref|, and the following +function should be used to generate from it: + += +void CReferences::A1_as_ref(code_generation *gen, inter_tree_node *P) { + C_GEN_DATA(memdata.next_node_is_a_ref) = TRUE; + CodeGen::FC::frame(gen, InterTree::first_child(P)); + C_GEN_DATA(memdata.next_node_is_a_ref) = FALSE; +} + +@ That sets a temporary mode which is immediately detected and cleared by +the generator for whatever A1 actually is. That generator is expected to call +this function to detect whether it's a ref. In this mode, A1 is compiled not +to a valid C expression to evaluate the contents of A1, but instead to a +function call which will modify A1, and which is missing one or two final +arguments. + +Note that the mode is auto-exited at once. This is all a bit clumsy, but is +correct. + += +int CReferences::am_I_a_ref(code_generation *gen) { + int answer = C_GEN_DATA(memdata.next_node_is_a_ref); + C_GEN_DATA(memdata.next_node_is_a_ref) = FALSE; + return answer; +} + +@ So, then, here goes: + += +int CReferences::compile_primitive(code_generation *gen, inter_ti bip, inter_tree_node *P) { + text_stream *OUT = CodeGen::current(gen); + text_stream *store_form = NULL; + switch (bip) { + case STORE_BIP: store_form = I"i7_lvalue_SET"; break; + case PREINCREMENT_BIP: store_form = I"i7_lvalue_PREINC"; break; + case POSTINCREMENT_BIP: store_form = I"i7_lvalue_POSTINC"; break; + case PREDECREMENT_BIP: store_form = I"i7_lvalue_PREDEC"; break; + case POSTDECREMENT_BIP: store_form = I"i7_lvalue_POSTDEC"; break; + case SETBIT_BIP: store_form = I"i7_lvalue_SETBIT"; break; + case CLEARBIT_BIP: store_form = I"i7_lvalue_CLEARBIT"; break; + default: return NOT_APPLICABLE; + } + if (store_form) @; + return FALSE; +} + +@ Some storage objects, like variables, can be generated to C code which works +in either an lvalue or rvalue context. For example, the Inter variable |frog| +generates just as the C variable |i7_mgl_frog|.[1] It's then fine to generate +code like either |10 + i7_mgl_frog|, where it is used in a |val| context, or +like |i7_mgl_frog++|, where it is used in a |ref| context. + +But other storage objects are not so lucky, and those need to generate to +different function calls, one used in a |ref| setting, one used in a |val|. +That's what is done by the "A1 as ref" mode set up above. + +[1] In real life, do not mangle frogs. See C. S. Lewis, "Perelandra", 1943. + +@ = + inter_tree_node *ref = InterTree::first_child(P); + if ((CodeGen::CL::node_is_word_array_ref(gen->from, ref)) || + (CodeGen::CL::node_is_property_ref(gen->from, ref))) { + @; + } else { + @; + } + +@ = + WRITE("("); CReferences::A1_as_ref(gen, P); + if (bip == STORE_BIP) { INV_A2; } else { WRITE("0"); } + WRITE(", %S))", store_form); + +@ = + switch (bip) { + case PREINCREMENT_BIP: WRITE("++("); INV_A1; WRITE(")"); break; + case POSTINCREMENT_BIP: WRITE("("); INV_A1; WRITE(")++"); break; + case PREDECREMENT_BIP: WRITE("--("); INV_A1; WRITE(")"); break; + case POSTDECREMENT_BIP: WRITE("("); INV_A1; WRITE(")--"); break; + case STORE_BIP: WRITE("("); INV_A1; WRITE(" = "); INV_A2; WRITE(")"); break; + case SETBIT_BIP: INV_A1; WRITE(" = "); INV_A1; WRITE(" | "); INV_A2; break; + case CLEARBIT_BIP: INV_A1; WRITE(" = "); INV_A1; WRITE(" &~ ("); INV_A2; WRITE(")"); break; + } diff --git a/inter/final-module/Chapter 5/Final C.w b/inter/final-module/Chapter 5/Final C.w index e376712b9..94f551f6c 100644 --- a/inter/final-module/Chapter 5/Final C.w +++ b/inter/final-module/Chapter 5/Final C.w @@ -2,7 +2,7 @@ To generate I6 code from intermediate code. -@h Target, +@h Target. = code_generation_target *c_target = NULL; @@ -12,6 +12,7 @@ void CTarget::create_target(void) { METHOD_ADD(c_target, BEGIN_GENERATION_MTID, CTarget::begin_generation); METHOD_ADD(c_target, END_GENERATION_MTID, CTarget::end_generation); + CMemoryModel::initialise(c_target); CObjectModel::initialise(c_target); METHOD_ADD(c_target, GENERAL_SEGMENT_MTID, CTarget::general_segment); @@ -40,13 +41,30 @@ void CTarget::create_target(void) { METHOD_ADD(c_target, BEGIN_OPCODE_MTID, CTarget::begin_opcode); METHOD_ADD(c_target, SUPPLY_OPERAND_MTID, CTarget::supply_operand); METHOD_ADD(c_target, END_OPCODE_MTID, CTarget::end_opcode); - METHOD_ADD(c_target, BEGIN_ARRAY_MTID, CTarget::begin_array); - METHOD_ADD(c_target, ARRAY_ENTRY_MTID, CTarget::array_entry); - METHOD_ADD(c_target, END_ARRAY_MTID, CTarget::end_array); METHOD_ADD(c_target, OFFER_PRAGMA_MTID, CTarget::offer_pragma) METHOD_ADD(c_target, NEW_FAKE_ACTION_MTID, CTarget::new_fake_action); } +@h Static supporting code. +The C code generated here would not compile as a stand-alone file. It needs +to use variables and functions from a small unchanging library called +|inform7_clib.h|. (The |.h| there is questionable, since this is not purely +a header file: it contains actual content and not only predeclarations. On +the other hand, it serves the same basic purpose.) + +The code we generate here can only make sense if read alongside |inform7_clib.h|, +and vice versa, so the file is presented here in installments. This is the +first of those: + += (text to inform7_clib.h) +/* This is a library of C code to support Inform or other Inter programs compiled + tp ANSI C. It was generated mechanically from the Inter source code, so to + change it, edit that and not this. */ + +#include +#include += + @h Segmentation. @e c_fundamental_types_I7CGS @@ -119,19 +137,19 @@ int C_target_segments[] = { typedef struct C_generation_data { text_stream *double_quoted_C; int no_double_quoted_C_strings; - int extent_of_i7mem; int C_dword_count; int C_action_count; struct dictionary *C_vm_dictionary; + struct C_generation_memory_model_data memdata; struct C_generation_object_model_data objdata; CLASS_DEFINITION } C_generation_data; void CTarget::initialise_data(code_generation *gen) { + CMemoryModel::initialise_data(gen); CObjectModel::initialise_data(gen); C_GEN_DATA(double_quoted_C) = Str::new(); C_GEN_DATA(no_double_quoted_C_strings) = 0; - C_GEN_DATA(extent_of_i7mem) = 0; C_GEN_DATA(C_dword_count) = 0; C_GEN_DATA(C_action_count) = 0; C_GEN_DATA(C_vm_dictionary) = Dictionaries::new(1024, TRUE); @@ -157,7 +175,7 @@ int CTarget::begin_generation(code_generation_target *cgt, code_generation *gen) WRITE("#include \"inform7_clib.h\"\n"); CodeGen::deselect(gen, saved); - CTarget::begin_memory(gen); + CMemoryModel::begin(gen); CTarget::begin_functions(gen); CObjectModel::begin(gen); CTarget::begin_dictionary_words(gen); @@ -166,7 +184,7 @@ int CTarget::begin_generation(code_generation_target *cgt, code_generation *gen) } int CTarget::end_generation(code_generation_target *cgt, code_generation *gen) { - CTarget::end_memory(gen); + CMemoryModel::end(gen); CTarget::end_functions(gen); CObjectModel::end(gen); CTarget::end_dictionary_words(gen); diff --git a/inter/final-module/Contents.w b/inter/final-module/Contents.w index a19234ca5..f1cf21946 100644 --- a/inter/final-module/Contents.w +++ b/inter/final-module/Contents.w @@ -25,5 +25,7 @@ Chapter 4: Inform 6 Chapter 5: C Final C + C References + C Memory Model C Implementation C Object Model