static tsf_bool_t generate_parser()

in benchmarks/JetStream2/wasm/TSF/tsf_gpc_code_gen.c [634:1785]


static tsf_bool_t generate_parser(gpc_proto_t *proto,
                                  tsf_type_t *dest_type,
                                  tsf_type_t *src_type,
                                  gpc_cell_t offset,
                                  gpc_cell_t label,
                                  tsf_bool_t do_bounds_check) {
    uint32_t i, bit = 0, num_bits = 0;
    gpc_cell_t loop_label, done_label, really_done_label, label_offset;
    gpc_cell_t *targets;
    uint32_t static_size;
    tsf_bool_t result;
    tsf_bool_t instanceof;
    
    static_size = tsf_type_get_static_size(src_type);

    if (do_bounds_check && static_size != UINT32_MAX) {
        if (static_size > 0) {
            C(append(proto, GPC_I_BC, static_size));
        }
        do_bounds_check = tsf_false;
    }
    
    instanceof = tsf_type_instanceof(src_type, dest_type);
    
    if (!instanceof) {
	C(generate_set_default(proto, dest_type, offset));
    }
    
    if (dest_type->kind_code == TSF_TK_VOID || !instanceof) {
	/* this is the skip parser. */

        tsf_type_t *type = src_type;  /* makes things simpler since we
                                       * won't refer to dest_type at this
                                       * point. */
        
        if (static_size != UINT32_MAX) {
            if (static_size > 0) {
                C(append(proto,
                         GPC_I_SKIP,
                         (gpc_cell_t)static_size));
            }
            return tsf_true;
        }
        
        /* interesting observation: from here on, do_bounds_check must be true.
         * because if do_bounds_check was false, then that means that a bounds
         * check was possible, which means in turn that our size is statically
         * known. */
        tsf_assert(do_bounds_check);
        
        switch (type->kind_code) {
        case TSF_TK_INTEGER:
            C(append(proto, GPC_I_TSF_INTEGER_SKIP));
            break;
        case TSF_TK_LONG:
            C(append(proto, GPC_I_TSF_LONG_SKIP));
            break;
	case TSF_TK_STRUCT:
	    for (i = 0; i < tsf_struct_type_get_num_elements(type); ++i) {
		tsf_named_type_t *n = tsf_struct_type_get_element(type, i);
		if (n->type->kind_code == TSF_TK_BIT) {
		    num_bits++;
		    continue;
		}
		C(generate_parser(proto,
				  tsf_type_create(TSF_TK_VOID),
				  n->type,
				  0,
				  label,
				  tsf_true));
	    }
	    if (num_bits) {
		C(append(proto,
			 GPC_I_SKIP,
			 (gpc_cell_t)((num_bits + 7) >> 3)));
	    }
	    break;
	case TSF_TK_ARRAY:
	    if (type->u.a.element_type->kind_code == TSF_TK_VOID) {
		C(append(proto, GPC_I_ARRAY_LEN_SKIP));
	    } else if (type->u.a.element_type->kind_code == TSF_TK_BIT) {
		C(append(proto, GPC_I_BITVECTOR_SKIP));
	    } else {
		static_size =
		    tsf_type_get_static_size(type->u.a.element_type);
		if (static_size == UINT32_MAX) {
		    C(append(proto, GPC_I_ARRAY_LEN_READ_LOCAL));

		    loop_label = label++;
		    done_label = label++;
                        
		    C(append(proto, GPC_I_ZEROJUMP, done_label));
		    C(append(proto, GPC_I_LABEL, loop_label));
		    C(generate_parser(proto,
				      tsf_type_create(TSF_TK_VOID),
				      type->u.a.element_type,
				      0,
				      label,
				      tsf_true));
		    C(append(proto, GPC_I_DECCOMPJUMP, loop_label));
		    C(append(proto, GPC_I_LABEL, done_label));
		    C(append(proto, GPC_I_POP));
		} else {
		    C(append(proto,
			     GPC_I_ARRAY_SKIP,
			     (gpc_cell_t)static_size));
		}
	    }
	    break;
	case TSF_TK_CHOICE:
	    tsf_assert(tsf_choice_type_has_non_void(type) ||
                       (tsf_choice_type_get_num_elements(type) >= 256 &&
                        !type->u.h.choice_as_full_word));
                
	    if (tsf_choice_type_get_num_elements(type) >= 256) {
                if (type->u.h.choice_as_full_word) {
                    C(append(proto, GPC_I_BC, 4));
                    C(append(proto, GPC_I_READL));
                } else {
                    if (tsf_choice_type_has_non_void(type)) {
                        C(append(proto, GPC_I_TSF_UNSIGNED_READ_SUB1));
                    } else {
                        C(append(proto, GPC_I_TSF_UNSIGNED_SKIP));
                    }
                }
	    } else {
		C(append(proto, GPC_I_BC, 1));
		C(append(proto, GPC_I_READC_TO_CHOICE));
	    }
            
            if (tsf_choice_type_has_non_void(type)) {
                label_offset = label;
                label += tsf_choice_type_get_num_elements(type);
                done_label = label++;
                targets=malloc(sizeof(gpc_cell_t) *
                               tsf_choice_type_get_num_elements(type));
                if (targets == NULL) {
                    tsf_set_errno("Could not malloc array of "
                                  "gpc_cell_t");
                    return tsf_false;
                }
                for (i = 0; i < tsf_choice_type_get_num_elements(type); ++i) {
                    targets[i] = label_offset + i;
                }
                result = append_tablejump_local(
                    proto,
                    tsf_choice_type_get_num_elements(type),
                    targets);
                free(targets);
                if (!result) {
                    return tsf_false;
                }
                
                /* for unknown (default) values */
                C(append(proto, GPC_I_JUMP, done_label));
                
                for (i = 0; i < tsf_choice_type_get_num_elements(type); ++i) {
                    tsf_named_type_t *n = tsf_choice_type_get_element(type, i);
                    C(append(proto, GPC_I_LABEL, label_offset + i));
                    C(generate_parser(proto,
                                      tsf_type_create(TSF_TK_VOID),
                                      n->type,
                                      0,
                                      label,
                                      tsf_true));
                    C(append(proto, GPC_I_JUMP, done_label));
                }
                C(append(proto, GPC_I_LABEL, done_label));
            }
	    break;
	case TSF_TK_STRING:
	    C(append(proto, GPC_I_STRING_SKIP));
	    break;
	case TSF_TK_ANY:
	    C(append(proto, GPC_I_ANY_SKIP));
	    break;
	default:
	    tsf_abort("There appears to be a kind code that we don't "
		      "know about");
	    break;
        }
        
        return tsf_true;
    }
    
    switch (src_type->kind_code) {
    case TSF_TK_VOID:
	break;
    case TSF_TK_INT8:
	switch (dest_type->kind_code) {
	case TSF_TK_BIT:
	    C(append(proto, GPC_I_COPY_NTOHC_TO_BIT_INCSRC, offset));
	    break;
	case TSF_TK_INT8:
	case TSF_TK_UINT8:
	    C(append(proto, GPC_I_COPY_NTOHC_INCSRC, offset));
	    break;
	case TSF_TK_UINT16:
	case TSF_TK_INT16:
	    C(append(proto, GPC_I_COPY_NTOHC_TO_S_E_INCSRC, offset));
	    break;
	case TSF_TK_UINT32:
	case TSF_TK_INT32:
        case TSF_TK_INTEGER:
	    C(append(proto, GPC_I_COPY_NTOHC_TO_L_E_INCSRC, offset));
	    break;
	case TSF_TK_UINT64:
	case TSF_TK_INT64:
        case TSF_TK_LONG:
	    C(append(proto, GPC_I_COPY_NTOHC_TO_LL_E_INCSRC, offset));
	    break;
	case TSF_TK_FLOAT:
	    C(append(proto, GPC_I_COPY_NTOHC_TO_F_E_INCSRC, offset));
	    break;
	case TSF_TK_DOUBLE:
	    C(append(proto, GPC_I_COPY_NTOHC_TO_D_E_INCSRC, offset));
	    break;
	default:
	    tsf_abort("Bad src_type/dest_type combination; "
		      "should have been caught by "
		      "tsf_type_instanceof()");
	    break;
	}
	break;
    case TSF_TK_UINT8:
	switch (dest_type->kind_code) {
	case TSF_TK_BIT:
	    C(append(proto, GPC_I_COPY_NTOHC_TO_BIT_INCSRC, offset));
	    break;
	case TSF_TK_INT8:
	case TSF_TK_UINT8:
	    C(append(proto, GPC_I_COPY_NTOHC_INCSRC, offset));
	    break;
	case TSF_TK_UINT16:
	case TSF_TK_INT16:
	    C(append(proto, GPC_I_COPY_NTOHC_TO_S_Z_INCSRC, offset));
	    break;
	case TSF_TK_UINT32:
	case TSF_TK_INT32:
        case TSF_TK_INTEGER:
	    C(append(proto, GPC_I_COPY_NTOHC_TO_L_Z_INCSRC, offset));
	    break;
	case TSF_TK_UINT64:
	case TSF_TK_INT64:
        case TSF_TK_LONG:
	    C(append(proto, GPC_I_COPY_NTOHC_TO_LL_Z_INCSRC, offset));
	    break;
	case TSF_TK_FLOAT:
	    C(append(proto, GPC_I_COPY_NTOHC_TO_F_Z_INCSRC, offset));
	    break;
	case TSF_TK_DOUBLE:
	    C(append(proto, GPC_I_COPY_NTOHC_TO_D_Z_INCSRC, offset));
	    break;
	default:
	    tsf_abort("Bad src_type/dest_type combination; "
		      "should have been caught by "
		      "tsf_type_instanceof()");
	    break;
	}
	break;
    case TSF_TK_INT16:
	switch (dest_type->kind_code) {
	case TSF_TK_BIT:
	    C(append(proto, GPC_I_COPY_NTOHS_TO_BIT_INCSRC, offset));
	    break;
	case TSF_TK_INT8:
	case TSF_TK_UINT8:
	    C(append(proto, GPC_I_COPY_NTOHS_TO_C_INCSRC, offset));
	    break;
	case TSF_TK_UINT16:
	case TSF_TK_INT16:
	    C(append(proto, GPC_I_COPY_NTOHS_INCSRC, offset));
	    break;
	case TSF_TK_UINT32:
	case TSF_TK_INT32:
        case TSF_TK_INTEGER:
	    C(append(proto, GPC_I_COPY_NTOHS_TO_L_E_INCSRC, offset));
	    break;
	case TSF_TK_UINT64:
	case TSF_TK_INT64:
        case TSF_TK_LONG:
	    C(append(proto, GPC_I_COPY_NTOHS_TO_LL_E_INCSRC, offset));
	    break;
	case TSF_TK_FLOAT:
	    C(append(proto, GPC_I_COPY_NTOHS_TO_F_E_INCSRC, offset));
	    break;
	case TSF_TK_DOUBLE:
	    C(append(proto, GPC_I_COPY_NTOHS_TO_D_E_INCSRC, offset));
	    break;
	default:
	    tsf_abort("Bad src_type/dest_type combination; "
		      "should have been caught by "
		      "tsf_type_instanceof()");
	    break;
	}
	break;
    case TSF_TK_UINT16:
	switch (dest_type->kind_code) {
	case TSF_TK_BIT:
	    C(append(proto, GPC_I_COPY_NTOHS_TO_BIT_INCSRC, offset));
	    break;
	case TSF_TK_INT8:
	case TSF_TK_UINT8:
	    C(append(proto, GPC_I_COPY_NTOHS_TO_C_INCSRC, offset));
	    break;
	case TSF_TK_UINT16:
	case TSF_TK_INT16:
	    C(append(proto, GPC_I_COPY_NTOHS_INCSRC, offset));
	    break;
	case TSF_TK_UINT32:
	case TSF_TK_INT32:
        case TSF_TK_INTEGER:
	    C(append(proto, GPC_I_COPY_NTOHS_TO_L_Z_INCSRC, offset));
	    break;
	case TSF_TK_UINT64:
	case TSF_TK_INT64:
        case TSF_TK_LONG:
	    C(append(proto, GPC_I_COPY_NTOHS_TO_LL_Z_INCSRC, offset));
	    break;
	case TSF_TK_FLOAT:
	    C(append(proto, GPC_I_COPY_NTOHS_TO_F_Z_INCSRC, offset));
	    break;
	case TSF_TK_DOUBLE:
	    C(append(proto, GPC_I_COPY_NTOHS_TO_D_Z_INCSRC, offset));
	    break;
	default:
	    tsf_abort("Bad src_type/dest_type combination; "
		      "should have been caught by "
		      "tsf_type_instanceof()");
	    break;
	}
	break;
    case TSF_TK_INT32:
	switch (dest_type->kind_code) {
	case TSF_TK_BIT:
	    C(append(proto, GPC_I_COPY_NTOHL_TO_BIT_INCSRC, offset));
	    break;
	case TSF_TK_INT8:
	case TSF_TK_UINT8:
	    C(append(proto, GPC_I_COPY_NTOHL_TO_C_INCSRC, offset));
	    break;
	case TSF_TK_UINT16:
	case TSF_TK_INT16:
	    C(append(proto, GPC_I_COPY_NTOHL_TO_S_INCSRC, offset));
	    break;
	case TSF_TK_UINT32:
	case TSF_TK_INT32:
        case TSF_TK_INTEGER:
	    C(append(proto, GPC_I_COPY_NTOHL_INCSRC, offset));
	    break;
	case TSF_TK_UINT64:
	case TSF_TK_INT64:
        case TSF_TK_LONG:
	    C(append(proto, GPC_I_COPY_NTOHL_TO_LL_E_INCSRC, offset));
	    break;
	case TSF_TK_FLOAT:
	    C(append(proto, GPC_I_COPY_NTOHL_TO_F_E_INCSRC, offset));
	    break;
	case TSF_TK_DOUBLE:
	    C(append(proto, GPC_I_COPY_NTOHL_TO_D_E_INCSRC, offset));
	    break;
	default:
	    tsf_abort("Bad src_type/dest_type combination; "
		      "should have been caught by "
		      "tsf_type_instanceof()");
	    break;
	}
	break;
    case TSF_TK_UINT32:
	switch (dest_type->kind_code) {
	case TSF_TK_BIT:
	    C(append(proto, GPC_I_COPY_NTOHL_TO_BIT_INCSRC, offset));
	    break;
	case TSF_TK_INT8:
	case TSF_TK_UINT8:
	    C(append(proto, GPC_I_COPY_NTOHL_TO_C_INCSRC, offset));
	    break;
	case TSF_TK_UINT16:
	case TSF_TK_INT16:
	    C(append(proto, GPC_I_COPY_NTOHL_TO_S_INCSRC, offset));
	    break;
	case TSF_TK_UINT32:
	case TSF_TK_INT32:
        case TSF_TK_INTEGER:
	    C(append(proto, GPC_I_COPY_NTOHL_INCSRC, offset));
	    break;
	case TSF_TK_UINT64:
	case TSF_TK_INT64:
        case TSF_TK_LONG:
	    C(append(proto, GPC_I_COPY_NTOHL_TO_LL_Z_INCSRC, offset));
	    break;
	case TSF_TK_FLOAT:
	    C(append(proto, GPC_I_COPY_NTOHL_TO_F_Z_INCSRC, offset));
	    break;
	case TSF_TK_DOUBLE:
	    C(append(proto, GPC_I_COPY_NTOHL_TO_D_Z_INCSRC, offset));
	    break;
	default:
	    tsf_abort("Bad src_type/dest_type combination; "
		      "should have been caught by "
		      "tsf_type_instanceof()");
	    break;
	}
	break;
    case TSF_TK_INT64:
	switch (dest_type->kind_code) {
	case TSF_TK_BIT:
	    C(append(proto, GPC_I_COPY_NTOHLL_TO_BIT_INCSRC, offset));
	    break;
	case TSF_TK_INT8:
	case TSF_TK_UINT8:
	    C(append(proto, GPC_I_COPY_NTOHLL_TO_C_INCSRC, offset));
	    break;
	case TSF_TK_UINT16:
	case TSF_TK_INT16:
	    C(append(proto, GPC_I_COPY_NTOHLL_TO_S_INCSRC, offset));
	    break;
	case TSF_TK_UINT32:
	case TSF_TK_INT32:
        case TSF_TK_INTEGER:
	    C(append(proto, GPC_I_COPY_NTOHLL_TO_L_INCSRC, offset));
	    break;
	case TSF_TK_UINT64:
	case TSF_TK_INT64:
        case TSF_TK_LONG:
	    C(append(proto, GPC_I_COPY_NTOHLL_INCSRC, offset));
	    break;
	case TSF_TK_FLOAT:
	    C(append(proto, GPC_I_COPY_NTOHLL_TO_F_E_INCSRC, offset));
	    break;
	case TSF_TK_DOUBLE:
	    C(append(proto, GPC_I_COPY_NTOHLL_TO_D_E_INCSRC, offset));
	    break;
	default:
	    tsf_abort("Bad src_type/dest_type combination; "
		      "should have been caught by "
		      "tsf_type_instanceof()");
	    break;
	}
	break;
    case TSF_TK_UINT64:
	switch (dest_type->kind_code) {
	case TSF_TK_BIT:
	    C(append(proto, GPC_I_COPY_NTOHLL_TO_BIT_INCSRC, offset));
	    break;
	case TSF_TK_INT8:
	case TSF_TK_UINT8:
	    C(append(proto, GPC_I_COPY_NTOHLL_TO_C_INCSRC, offset));
	    break;
	case TSF_TK_UINT16:
	case TSF_TK_INT16:
	    C(append(proto, GPC_I_COPY_NTOHLL_TO_S_INCSRC, offset));
	    break;
	case TSF_TK_UINT32:
	case TSF_TK_INT32:
        case TSF_TK_INTEGER:
	    C(append(proto, GPC_I_COPY_NTOHLL_TO_L_INCSRC, offset));
	    break;
	case TSF_TK_UINT64:
	case TSF_TK_INT64:
        case TSF_TK_LONG:
	    C(append(proto, GPC_I_COPY_NTOHLL_INCSRC, offset));
	    break;
	case TSF_TK_FLOAT:
	    C(append(proto, GPC_I_COPY_NTOHLL_TO_F_Z_INCSRC, offset));
	    break;
	case TSF_TK_DOUBLE:
	    C(append(proto, GPC_I_COPY_NTOHLL_TO_D_Z_INCSRC, offset));
	    break;
	default:
	    tsf_abort("Bad src_type/dest_type combination; "
		      "should have been caught by "
		      "tsf_type_instanceof()");
	    break;
	}
	break;
    case TSF_TK_FLOAT:
	switch (dest_type->kind_code) {
	case TSF_TK_BIT:
	    C(append(proto, GPC_I_COPY_NTOHF_TO_BIT_INCSRC, offset));
	    break;
	case TSF_TK_INT8:
	    C(append(proto, GPC_I_COPY_NTOHF_TO_C_E_INCSRC, offset));
	    break;
	case TSF_TK_UINT8:
	    C(append(proto, GPC_I_COPY_NTOHF_TO_C_Z_INCSRC, offset));
	    break;
	case TSF_TK_INT16:
	    C(append(proto, GPC_I_COPY_NTOHF_TO_S_E_INCSRC, offset));
	    break;
	case TSF_TK_UINT16:
	    C(append(proto, GPC_I_COPY_NTOHF_TO_S_Z_INCSRC, offset));
	    break;
	case TSF_TK_INT32:
        case TSF_TK_INTEGER:
	    C(append(proto, GPC_I_COPY_NTOHF_TO_L_E_INCSRC, offset));
	    break;
	case TSF_TK_UINT32:
	    C(append(proto, GPC_I_COPY_NTOHF_TO_L_Z_INCSRC, offset));
	    break;
	case TSF_TK_INT64:
        case TSF_TK_LONG:
	    C(append(proto, GPC_I_COPY_NTOHF_TO_LL_E_INCSRC, offset));
	    break;
	case TSF_TK_UINT64:
	    C(append(proto, GPC_I_COPY_NTOHF_TO_LL_Z_INCSRC, offset));
	    break;
	case TSF_TK_FLOAT:
	    C(append(proto, GPC_I_COPY_NTOHF_INCSRC, offset));
	    break;
	case TSF_TK_DOUBLE:
	    C(append(proto, GPC_I_COPY_NTOHF_TO_D_INCSRC, offset));
	    break;
	default:
	    tsf_abort("Bad src_type/dest_type combination; "
		      "should have been caught by "
		      "tsf_type_instanceof()");
	    break;
	}
	break;
    case TSF_TK_DOUBLE:
	switch (dest_type->kind_code) {
	case TSF_TK_BIT:
	    C(append(proto, GPC_I_COPY_NTOHD_TO_BIT_INCSRC, offset));
	    break;
	case TSF_TK_INT8:
	    C(append(proto, GPC_I_COPY_NTOHD_TO_C_E_INCSRC, offset));
	    break;
	case TSF_TK_UINT8:
	    C(append(proto, GPC_I_COPY_NTOHD_TO_C_Z_INCSRC, offset));
	    break;
	case TSF_TK_INT16:
	    C(append(proto, GPC_I_COPY_NTOHD_TO_S_E_INCSRC, offset));
	    break;
	case TSF_TK_UINT16:
	    C(append(proto, GPC_I_COPY_NTOHD_TO_S_Z_INCSRC, offset));
	    break;
	case TSF_TK_INT32:
        case TSF_TK_INTEGER:
	    C(append(proto, GPC_I_COPY_NTOHD_TO_L_E_INCSRC, offset));
	    break;
	case TSF_TK_UINT32:
	    C(append(proto, GPC_I_COPY_NTOHD_TO_L_Z_INCSRC, offset));
	    break;
	case TSF_TK_INT64:
        case TSF_TK_LONG:
	    C(append(proto, GPC_I_COPY_NTOHD_TO_LL_E_INCSRC, offset));
	    break;
	case TSF_TK_UINT64:
	    C(append(proto, GPC_I_COPY_NTOHD_TO_LL_Z_INCSRC, offset));
	    break;
	case TSF_TK_FLOAT:
	    C(append(proto, GPC_I_COPY_NTOHD_TO_F_INCSRC, offset));
	    break;
	case TSF_TK_DOUBLE:
	    C(append(proto, GPC_I_COPY_NTOHD_INCSRC, offset));
	    break;
	default:
	    tsf_abort("Bad src_type/dest_type combination; "
		      "should have been caught by "
		      "tsf_type_instanceof()");
	    break;
	}
	break;
    case TSF_TK_INTEGER:
        tsf_assert(do_bounds_check);
	switch (dest_type->kind_code) {
	case TSF_TK_BIT:
	    C(append(proto, GPC_I_TSF_INTEGER_READ_TO_BIT, offset));
	    break;
	case TSF_TK_INT8:
	case TSF_TK_UINT8:
	    C(append(proto, GPC_I_TSF_INTEGER_READ_TO_C, offset));
	    break;
	case TSF_TK_INT16:
	case TSF_TK_UINT16:
	    C(append(proto, GPC_I_TSF_INTEGER_READ_TO_S, offset));
	    break;
	case TSF_TK_INT32:
	case TSF_TK_UINT32:
        case TSF_TK_INTEGER:
	    C(append(proto, GPC_I_TSF_INTEGER_READ, offset));
	    break;
	case TSF_TK_INT64:
	case TSF_TK_UINT64:
        case TSF_TK_LONG:
	    C(append(proto, GPC_I_TSF_INTEGER_READ_TO_LL, offset));
	    break;
	case TSF_TK_FLOAT:
	    C(append(proto, GPC_I_TSF_INTEGER_READ_TO_F, offset));
	    break;
	case TSF_TK_DOUBLE:
	    C(append(proto, GPC_I_TSF_INTEGER_READ_TO_D, offset));
	    break;
	default:
	    tsf_abort("Bad src_type/dest_type combination; "
		      "should have been caught by "
		      "tsf_type_instanceof()");
	    break;
	}
	break;
    case TSF_TK_LONG:
        tsf_assert(do_bounds_check);
	switch (dest_type->kind_code) {
	case TSF_TK_BIT:
	    C(append(proto, GPC_I_TSF_LONG_READ_TO_BIT, offset));
	    break;
	case TSF_TK_INT8:
	case TSF_TK_UINT8:
	    C(append(proto, GPC_I_TSF_LONG_READ_TO_C, offset));
	    break;
	case TSF_TK_INT16:
	case TSF_TK_UINT16:
	    C(append(proto, GPC_I_TSF_LONG_READ_TO_S, offset));
	    break;
	case TSF_TK_INT32:
	case TSF_TK_UINT32:
        case TSF_TK_INTEGER:
	    C(append(proto, GPC_I_TSF_LONG_READ_TO_L, offset));
	    break;
	case TSF_TK_INT64:
	case TSF_TK_UINT64:
        case TSF_TK_LONG:
	    C(append(proto, GPC_I_TSF_LONG_READ, offset));
	    break;
	case TSF_TK_FLOAT:
	    C(append(proto, GPC_I_TSF_LONG_READ_TO_F, offset));
	    break;
	case TSF_TK_DOUBLE:
	    C(append(proto, GPC_I_TSF_LONG_READ_TO_D, offset));
	    break;
	default:
	    tsf_abort("Bad src_type/dest_type combination; "
		      "should have been caught by "
		      "tsf_type_instanceof()");
	    break;
	}
	break;
    case TSF_TK_BIT:
	switch (dest_type->kind_code) {
	case TSF_TK_INT8:
	case TSF_TK_UINT8:
	case TSF_TK_BIT:
	    C(append(proto, GPC_I_BIT_READ, offset));
	    break;
	case TSF_TK_INT16:
	case TSF_TK_UINT16:
	    C(append(proto, GPC_I_BIT_READ_TO_S, offset));
	    break;
	case TSF_TK_INT32:
	case TSF_TK_UINT32:
        case TSF_TK_INTEGER:
	    C(append(proto, GPC_I_BIT_READ_TO_L, offset));
	    break;
	case TSF_TK_INT64:
	case TSF_TK_UINT64:
        case TSF_TK_LONG:
	    C(append(proto, GPC_I_BIT_READ_TO_LL, offset));
	    break;
	case TSF_TK_FLOAT:
	    C(append(proto, GPC_I_BIT_READ_TO_F, offset));
	    break;
	case TSF_TK_DOUBLE:
	    C(append(proto, GPC_I_BIT_READ_TO_D, offset));
	    break;
	default:
	    tsf_abort("Bad src_type/dest_type combination; "
		      "should have been caught by "
		      "tsf_type_instanceof()");
	    break;
	}
	break;
    case TSF_TK_STRUCT:
	if (dest_type->u.s.constructor != NULL) {
	    C(append(proto, GPC_I_CALL,
		     offset,
		     dest_type->u.s.constructor));
	}
	if (dest_type->u.s.pre_destructor != NULL) {
	    C(append(proto, GPC_I_ADD_CBACK,
		     offset,
		     dest_type->u.s.pre_destructor));
	}

	for (i = 0; i < tsf_struct_type_get_num_elements(dest_type); ++i) {
	    tsf_named_type_t *dn = tsf_struct_type_get_element(dest_type, i);
	    if (tsf_struct_type_find_node(src_type, dn->name) == NULL) {
		C(generate_set_default(proto, dn->type, offset + dn->offset));
	    }
	}
	for (i = 0; i < tsf_struct_type_get_num_elements(src_type); ++i) {
	    tsf_named_type_t *sn = tsf_struct_type_get_element(src_type, i);
	    tsf_named_type_t *dn;
	    if (sn->type->kind_code == TSF_TK_BIT) {
		num_bits++;
		continue;
	    }
	    dn = tsf_struct_type_find_node(dest_type, sn->name);
	    if (dn == NULL) {
		/* skip */
		C(generate_parser(proto,
				  tsf_type_create(TSF_TK_VOID),
				  sn->type,
				  0,
				  label,
				  do_bounds_check));
	    } else {
		/* read it */
		C(generate_parser(proto,
				  dn->type,
				  sn->type,
				  offset + dn->offset,
				  label,
				  do_bounds_check));
	    }
	}
            
	if (num_bits) {
	    if (do_bounds_check) {
		C(append(proto,
			 GPC_I_BC,
			 (gpc_cell_t)((num_bits + 7) >> 3)));
	    }
                
	    for (i = 0; i < tsf_struct_type_get_num_elements(src_type); ++i) {
		tsf_named_type_t *sn =
		    tsf_struct_type_get_element(src_type, i);
		tsf_named_type_t *dn;
		if (sn->type->kind_code != TSF_TK_BIT) {
		    continue;
		}
		dn = tsf_struct_type_find_node(dest_type, sn->name);
		if (dn != NULL) {
		    switch (dn->type->kind_code) {
		    case TSF_TK_VOID:
			break;
		    case TSF_TK_BIT:
		    case TSF_TK_INT8:
		    case TSF_TK_UINT8:
			C(append(proto,
				 GPC_I_BIT_MASK_READ,
				 bit,
				 offset + dn->offset));
			break;
		    case TSF_TK_INT16:
		    case TSF_TK_UINT16:
			C(append(proto,
				 GPC_I_BIT_MASK_READ_TO_S,
				 bit,
				 offset + dn->offset));
			break;
		    case TSF_TK_INT32:
		    case TSF_TK_UINT32:
                    case TSF_TK_INTEGER:
			C(append(proto,
				 GPC_I_BIT_MASK_READ_TO_L,
				 bit,
				 offset + dn->offset));
			break;
		    case TSF_TK_INT64:
		    case TSF_TK_UINT64:
                    case TSF_TK_LONG:
			C(append(proto,
				 GPC_I_BIT_MASK_READ_TO_LL,
				 bit,
				 offset + dn->offset));
			break;
		    case TSF_TK_FLOAT:
			C(append(proto,
				 GPC_I_BIT_MASK_READ_TO_F,
				 bit,
				 offset + dn->offset));
			break;
		    case TSF_TK_DOUBLE:
			C(append(proto,
				 GPC_I_BIT_MASK_READ_TO_D,
				 bit,
				 offset + dn->offset));
			break;
		    default:
			tsf_abort("Bad src_type/dest_type combination; "
				  "should have been caught by "
				  "tsf_type_instanceof()");
			break;
		    }
		}
		++bit;
		if (bit == 8) {
		    bit = 0;
		    C(append(proto, GPC_I_SKIP, 1));
		}
	    }
	    if (bit) {
		C(append(proto, GPC_I_SKIP, 1));
	    }
	}
	break;
    case TSF_TK_ARRAY:
	if (dest_type->u.a.element_type->kind_code == TSF_TK_VOID) {
	    C(append(proto,
		     GPC_I_ARRAY_LEN_READ_FIELD,
		     offset + tsf_offsetof(tsf_native_void_array_t,
                                           len)));
                
	    if (src_type->u.a.element_type->kind_code == TSF_TK_BIT) {
		C(append(proto,
			 GPC_I_BITVECTOR_BC_AND_SKIP_FIELD,
			 offset + tsf_offsetof(tsf_native_void_array_t,
                                               len)));
	    } else if (src_type->u.a.element_type->kind_code
		       == TSF_TK_VOID) {
		/* nothing to do */
	    } else {
		static_size =
		    tsf_type_get_static_size(src_type->u.a.element_type);
		if (static_size == UINT32_MAX) {
		    loop_label = label++;
		    done_label = label++;
                        
		    C(append(proto,
			     GPC_I_PUSH_VAL,
			     offset + tsf_offsetof(tsf_native_void_array_t,
                                                   len)));
                        
		    C(append(proto, GPC_I_ZEROJUMP, done_label));
		    C(append(proto, GPC_I_LABEL, loop_label));
		    C(generate_parser(proto,
				      tsf_type_create(TSF_TK_VOID),
				      src_type->u.a.element_type,
				      0,
				      label,
				      tsf_true));
		    C(append(proto, GPC_I_DECCOMPJUMP, loop_label));
		    C(append(proto, GPC_I_LABEL, done_label));
                        
		    C(append(proto, GPC_I_POP));
		} else {
		    C(append(proto,
			     GPC_I_ARRAY_BC_AND_SKIP_FIELD,
			     offset + tsf_offsetof(tsf_native_void_array_t,
                                                   len),
			     static_size));
		}
	    }
	} else if (dest_type->u.a.element_type->kind_code == TSF_TK_BIT) {
	    switch (src_type->u.a.element_type->kind_code) {
	    case TSF_TK_BIT:
		C(append(proto,
			 GPC_I_BITVECTOR_READ,
			 offset));
		break;
	    case TSF_TK_UINT8:
	    case TSF_TK_INT8:
		C(append(proto,
			 GPC_I_BITVECTOR_READ_FROM_C,
			 offset));
		break;
	    case TSF_TK_UINT16:
	    case TSF_TK_INT16:
		C(append(proto,
			 GPC_I_BITVECTOR_READ_FROM_S,
			 offset));
		break;
	    case TSF_TK_UINT32:
	    case TSF_TK_INT32:
		C(append(proto,
			 GPC_I_BITVECTOR_READ_FROM_L,
			 offset));
		break;
	    case TSF_TK_UINT64:
	    case TSF_TK_INT64:
		C(append(proto,
			 GPC_I_BITVECTOR_READ_FROM_LL,
			 offset));
		break;
            case TSF_TK_INTEGER:
                C(append(proto,
                         GPC_I_BITVECTOR_READ_FROM_TSF_INTEGER_ARRAY,
                         offset));
                break;
            case TSF_TK_LONG:
                C(append(proto,
                         GPC_I_BITVECTOR_READ_FROM_TSF_LONG_ARRAY,
                         offset));
                break;
	    case TSF_TK_FLOAT:
		C(append(proto,
			 GPC_I_BITVECTOR_READ_FROM_F,
			 offset));
		break;
	    case TSF_TK_DOUBLE:
		C(append(proto,
			 GPC_I_BITVECTOR_READ_FROM_D,
			 offset));
		break;
	    default:
		tsf_abort("odd case");
		break;
	    }
	} else if (src_type->u.a.element_type->kind_code == TSF_TK_BIT) {
	    switch (dest_type->u.a.element_type->kind_code) {
	    case TSF_TK_UINT8:
	    case TSF_TK_INT8:
		C(append(proto,
			 GPC_I_BITVECTOR_READ_TO_C,
			 offset));
		break;
	    case TSF_TK_UINT16:
	    case TSF_TK_INT16:
		C(append(proto,
			 GPC_I_BITVECTOR_READ_TO_S,
			 offset));
		break;
	    case TSF_TK_UINT32:
	    case TSF_TK_INT32:
            case TSF_TK_INTEGER:
		C(append(proto,
			 GPC_I_BITVECTOR_READ_TO_L,
			 offset));
		break;
	    case TSF_TK_UINT64:
	    case TSF_TK_INT64:
            case TSF_TK_LONG:
		C(append(proto,
			 GPC_I_BITVECTOR_READ_TO_LL,
			 offset));
		break;
	    case TSF_TK_FLOAT:
		C(append(proto,
			 GPC_I_BITVECTOR_READ_TO_F,
			 offset));
		break;
	    case TSF_TK_DOUBLE:
		C(append(proto,
			 GPC_I_BITVECTOR_READ_TO_D,
			 offset));
		break;
	    default:
		tsf_abort("odd case");
		break;
	    }
	} else if (src_type->u.a.element_type->kind_code
		   == dest_type->u.a.element_type->kind_code &&
		   (src_type->u.a.element_type->kind_code == TSF_TK_UINT8 ||
		    src_type->u.a.element_type->kind_code == TSF_TK_INT8
#ifndef NEED_INT_CONVERSION
		    || (tsf_type_kind_is_int(src_type->u.a.element_type->kind_code)
                        && src_type->u.a.element_type->kind_code != TSF_TK_INTEGER
                        && src_type->u.a.element_type->kind_code != TSF_TK_LONG)
#endif
#ifndef NEED_FLOAT_CONVERSION
		    || tsf_type_kind_is_float(src_type->u.a.element_type->kind_code)
#endif
                    )) {
	    C(append(proto,
		     GPC_I_BYTE_ARRAY_READ,
		     offset,
		     tsf_type_get_static_size(src_type->u.a.element_type)));
	} else {
	    C(append(proto,
		     GPC_I_ARRAY_LEN_READ_FIELD,
		     offset + tsf_offsetof(tsf_native_array_t, len)));
                
	    static_size = tsf_type_get_static_size(src_type->u.a.element_type);
	    if (static_size != UINT32_MAX) {
		C(append(proto,
			 GPC_I_ARRAY_BC_FIELD,
			 offset + tsf_offsetof(tsf_native_array_t, len),
			 static_size));
	    }
            
	    C(append(proto,
		     GPC_I_ALLOC_ARRAY,
		     offset,
		     tsf_native_type_get_size(dest_type->u.a.element_type)));
                
	    loop_label = label++;
	    done_label = label++;
                
	    C(append(proto,
		     GPC_I_REPUSH_MULADD_PTR,
		     offset + tsf_offsetof(tsf_native_array_t,len),
		     tsf_native_type_get_size(dest_type->u.a.element_type)));
	    C(append(proto,
		     GPC_I_COMPFAILJUMP,
		     done_label));
	    C(append(proto, GPC_I_LABEL, loop_label));
	    C(generate_parser(proto,
			      dest_type->u.a.element_type,
			      src_type->u.a.element_type,
			      0,
			      label,
			      static_size == UINT32_MAX));
	    C(append(proto,
		     GPC_I_ADDCOMPJUMP,
		     tsf_native_type_get_size(dest_type->u.a.element_type),
		     loop_label));
	    C(append(proto, GPC_I_LABEL, done_label));
	    C(append(proto, GPC_I_TWO_POP));
	}
	break;
    case TSF_TK_CHOICE:
	if (tsf_choice_type_get_num_elements(src_type) >= 256) {
            if (src_type->u.h.choice_as_full_word) {
                if (do_bounds_check) {
                    C(append(proto, GPC_I_BC, 4));
                }
                C(append(proto, GPC_I_READL));
            } else {
                C(append(proto, GPC_I_TSF_UNSIGNED_READ_SUB1));
            }
	} else {
	    if (do_bounds_check) {
		C(append(proto, GPC_I_BC, 1));
	    }
	    C(append(proto, GPC_I_READC_TO_CHOICE));
	}
            
	if (tsf_choice_type_has_non_void(src_type)) {
	    /* use tablejump */
	    label_offset = label;
	    label += tsf_choice_type_get_num_elements(src_type);
	    done_label = label++;
	    really_done_label = label++;
	    targets=malloc(sizeof(gpc_cell_t) *
			   tsf_choice_type_get_num_elements(src_type));
	    if (targets == NULL) {
		tsf_set_errno("Could not malloc array of "
			      "gpc_cell_t");
		return tsf_false;
	    }
	    for (i = 0; i < tsf_choice_type_get_num_elements(src_type); ++i) {
		targets[i] = label_offset + i;
	    }
	    result=append_tablejump_local(proto,
					  tsf_choice_type_get_num_elements(src_type),
					  targets);
	    free(targets);
	    if (!result) {
		return tsf_false;
	    }
	    C(append(proto,
		     GPC_I_SET_L,
		     (gpc_cell_t)(offset + dest_type->u.h.value_offset),
		     (gpc_cell_t)UINT32_MAX));
	    C(append(proto, GPC_I_JUMP, really_done_label));
	    for (i = 0; i < tsf_choice_type_get_num_elements(src_type); ++i) {
		tsf_named_type_t *sn =
		    tsf_choice_type_get_element(src_type, i);
		tsf_named_type_t *dn =
		    tsf_choice_type_find_node(dest_type, sn->name);
		C(append(proto, GPC_I_LABEL, label_offset + i));
		if (dn == NULL) {
		    C(append(proto,
			     GPC_I_SET_L,
			     (gpc_cell_t)offset + dest_type->u.h.value_offset,
			     (gpc_cell_t)UINT32_MAX));
		    C(generate_parser(proto,
				      tsf_type_create(TSF_TK_VOID),
				      sn->type,
				      0,
				      label,
				      tsf_true));
		    C(append(proto, GPC_I_JUMP, really_done_label));
		} else {
		    C(append(proto,
			     GPC_I_SET_L,
			     (gpc_cell_t)offset + dest_type->u.h.value_offset,
			     (gpc_cell_t)dn->index));
		    if (tsf_native_choice_type_is_in_place(dest_type)) {
			C(generate_parser(proto,
					  dn->type,
					  sn->type,
					  offset + dest_type->u.h.data_offset,
					  label,
					  tsf_true));
		    } else {
			C(append(proto,
				 GPC_I_ALLOC,
				 tsf_native_type_get_size(dn->type)));
			C(append(proto,
				 GPC_I_STORE_PTR,
				 offset + dest_type->u.h.data_offset));
			C(generate_parser(proto,
					  dn->type,
					  sn->type,
					  0,
					  label,
					  tsf_true));
		    }
		    C(append(proto, GPC_I_JUMP, done_label));
		}
	    }
	    C(append(proto, GPC_I_LABEL, done_label));
	    if (!tsf_native_choice_type_is_in_place(dest_type)) {
		C(append(proto, GPC_I_POP));
	    }
	    C(append(proto, GPC_I_LABEL, really_done_label));
	} else if (tsf_choice_type_uses_same_indices(src_type,
						     dest_type)) {
	    /* use checkchoice and store */
	    C(append(proto,
		     GPC_I_CHECKCHOICE,
		     tsf_choice_type_get_num_elements(src_type)));
	    C(append(proto,
		     GPC_I_STORE_VAL,
		     offset + dest_type->u.h.value_offset));
	    C(append(proto, GPC_I_POP));
	} else {
	    /* use tableset_local_to_field */
	    targets=malloc(sizeof(gpc_cell_t) *
			   tsf_choice_type_get_num_elements(src_type));
	    if (targets==NULL) {
		tsf_set_errno("Could not malloc array of "
			      "gpc_cell_t");
		return tsf_false;
	    }
	    for (i = 0; i < tsf_choice_type_get_num_elements(src_type); ++i) {
		tsf_named_type_t *sn =
		    tsf_choice_type_get_element(src_type, i);
		tsf_named_type_t *dn =
		    tsf_choice_type_find_node(dest_type, sn->name);
		if (dn == NULL) {
		    targets[i] = UINT32_MAX;
		} else {
		    targets[i] = dn->index;
		}
	    }
	    result = append_tableset_local_to_field(
                proto,
                offset + dest_type->u.h.value_offset,
                tsf_choice_type_get_num_elements(src_type),
                targets);
	    free(targets);
	    if (!result) {
		return tsf_false;
	    }
	}
	break;
    case TSF_TK_STRING:
	C(append(proto, GPC_I_STRING_READ, offset));
	break;
    case TSF_TK_ANY:
	C(append(proto, GPC_I_ANY_READ, offset));
	break;
    default:
	tsf_abort("Unknown type");
	break;
    }
    
    return tsf_true;
}