Example #1
0
bool gentrace_needed(ast_t* type)
{
  switch(trace_type(type))
  {
    case TRACE_NONE:
      assert(0);
      return false;

    case TRACE_PRIMITIVE:
      return false;

    case TRACE_TUPLE:
    {
      for(ast_t* child = ast_child(type);
        child != NULL;
        child = ast_sibling(child))
      {
        if(gentrace_needed(child))
          return true;
      }

      return false;
    }

    default:
      break;
  }

  return true;
}
Example #2
0
void gentrace(compile_t* c, LLVMValueRef ctx, LLVMValueRef value, ast_t* type)
{
  switch(trace_type(type))
  {
    case TRACE_NONE:
      assert(0);
      return;

    case TRACE_PRIMITIVE:
      return;

    case TRACE_MAYBE:
      trace_maybe(c, ctx, value, type);
      return;

    case TRACE_ACTOR:
      trace_actor(c, ctx, value);
      return;

    case TRACE_KNOWN_VAL:
      trace_known(c, ctx, value, type, true);
      return;

    case TRACE_UNKNOWN_VAL:
      trace_unknown(c, ctx, value, true);
      return;

    case TRACE_KNOWN:
      trace_known(c, ctx, value, type, false);
      return;

    case TRACE_UNKNOWN:
      trace_unknown(c, ctx, value, false);
      return;

    case TRACE_TAG:
      trace_tag(c, ctx, value);
      return;

    case TRACE_TAG_OR_ACTOR:
      trace_tag_or_actor(c, ctx, value);
      return;

    case TRACE_DYNAMIC:
    {
      LLVMBasicBlockRef next_block = codegen_block(c, "");
      trace_dynamic(c, ctx, value, type, type, NULL, next_block);
      LLVMBuildBr(c->builder, next_block);
      LLVMPositionBuilderAtEnd(c->builder, next_block);
      return;
    }

    case TRACE_TUPLE:
      trace_tuple(c, ctx, value, type);
      return;
  }
}
Example #3
0
static void trace_dynamic_nominal(compile_t* c, LLVMValueRef ctx,
  LLVMValueRef object, ast_t* type, ast_t* orig, ast_t* tuple,
  LLVMBasicBlockRef next_block)
{
  // Skip if a primitive.
  ast_t* def = (ast_t*)ast_data(type);

  if(ast_id(def) == TK_PRIMITIVE)
    return;

  // If it's not possible to use match or as to extract this type from the
  // original type, there's no need to trace as this type.
  if(tuple != NULL)
  {
    // We are a tuple element. Our type is in the correct position in the
    // tuple, everything else is TK_DONTCARE.
    if(is_matchtype(orig, tuple) != MATCHTYPE_ACCEPT)
      return;
  } else {
    // We aren't a tuple element.
    if(is_matchtype(orig, type) != MATCHTYPE_ACCEPT)
      return;
  }

  // We aren't always this type. We need to check dynamically.
  LLVMValueRef desc = gendesc_fetch(c, object);
  LLVMValueRef test = gendesc_isnominal(c, desc, type);

  LLVMBasicBlockRef is_true = codegen_block(c, "");
  LLVMBasicBlockRef is_false = codegen_block(c, "");
  LLVMBuildCondBr(c->builder, test, is_true, is_false);

  // Trace as this type.
  LLVMPositionBuilderAtEnd(c->builder, is_true);
  gentrace(c, ctx, object, type);

  // If we have traced as known, unknown or actor, we're done with this
  // element. Otherwise, continue tracing this as if the match had been
  // unsuccessful.
  switch(trace_type(type))
  {
    case TRACE_KNOWN:
    case TRACE_UNKNOWN:
    case TRACE_KNOWN_VAL:
    case TRACE_UNKNOWN_VAL:
    case TRACE_ACTOR:
      LLVMBuildBr(c->builder, next_block);
      break;

    default:
      LLVMBuildBr(c->builder, is_false);
      break;
  }

  // Carry on, whether we have traced or not.
  LLVMPositionBuilderAtEnd(c->builder, is_false);
}
Example #4
0
static trace_t trace_type_union(ast_t* type)
{
  trace_t trace = TRACE_NONE;

  for(ast_t* child = ast_child(type);
    child != NULL;
    child = ast_sibling(child))
  {
    trace_t t = trace_type(child);

    switch(trace)
    {
      case TRACE_NONE:
        trace = t;
        break;

      case TRACE_MAYBE:
        // Can't be in a union.
        pony_assert(0);
        return TRACE_NONE;

      case TRACE_MACHINE_WORD:
        trace = trace_union_machine_word(t);
        break;

      case TRACE_PRIMITIVE:
        trace = trace_union_primitive(t);
        break;

      case TRACE_MUT_KNOWN:
      case TRACE_MUT_UNKNOWN:
        trace = trace_union_mut(t);
        break;

      case TRACE_VAL_KNOWN:
      case TRACE_VAL_UNKNOWN:
        trace = trace_union_val(t);
        break;

      case TRACE_TAG_KNOWN:
      case TRACE_TAG_UNKNOWN:
        trace = trace_union_tag(t);
        break;

      case TRACE_DYNAMIC:
      case TRACE_TUPLE:
        return TRACE_DYNAMIC;

      case TRACE_STATIC:
        // Can't happen here.
        pony_assert(0);
        return TRACE_DYNAMIC;
    }
  }

  return trace;
}
Example #5
0
void gentrace(compile_t* c, LLVMValueRef ctx, LLVMValueRef value, ast_t* type)
{
  switch(trace_type(type))
  {
    case TRACE_NONE:
      assert(0);
      return;

    case TRACE_MACHINE_WORD:
    case TRACE_PRIMITIVE:
      return;

    case TRACE_MAYBE:
      trace_maybe(c, ctx, value, type);
      return;

    case TRACE_VAL_KNOWN:
      trace_known(c, ctx, value, type, PONY_TRACE_IMMUTABLE);
      return;

    case TRACE_VAL_UNKNOWN:
      trace_unknown(c, ctx, value, PONY_TRACE_IMMUTABLE);
      return;

    case TRACE_MUT_KNOWN:
      trace_known(c, ctx, value, type, PONY_TRACE_MUTABLE);
      return;

    case TRACE_MUT_UNKNOWN:
      trace_unknown(c, ctx, value, PONY_TRACE_MUTABLE);
      return;

    case TRACE_TAG_KNOWN:
      trace_known(c, ctx, value, type, PONY_TRACE_OPAQUE);
      return;

    case TRACE_TAG_UNKNOWN:
      trace_unknown(c, ctx, value, PONY_TRACE_OPAQUE);
      return;

    case TRACE_DYNAMIC:
    {
      LLVMBasicBlockRef next_block = codegen_block(c, "");
      trace_dynamic(c, ctx, value, type, type, NULL, next_block);
      LLVMBuildBr(c->builder, next_block);
      LLVMPositionBuilderAtEnd(c->builder, next_block);
      return;
    }

    case TRACE_TUPLE:
      trace_tuple(c, ctx, value, type);
      return;
  }
}
Example #6
0
static trace_t trace_type_isect(ast_t* type)
{
  trace_t trace = TRACE_DYNAMIC;

  for(ast_t* child = ast_child(type);
    child != NULL;
    child = ast_sibling(child))
  {
    trace_t t = trace_type(child);

    switch(t)
    {
      case TRACE_NONE:
      case TRACE_MAYBE:
        // Can't be in an isect.
        pony_assert(0);
        return TRACE_NONE;

      case TRACE_PRIMITIVE:
        return TRACE_PRIMITIVE;

      case TRACE_MACHINE_WORD:
      case TRACE_VAL_KNOWN:
      case TRACE_VAL_UNKNOWN:
        trace = TRACE_VAL_UNKNOWN;
        break;

      case TRACE_MUT_KNOWN:
      case TRACE_MUT_UNKNOWN:
        if(trace != TRACE_VAL_UNKNOWN)
          trace = TRACE_MUT_UNKNOWN;
        break;

      case TRACE_TAG_KNOWN:
      case TRACE_TAG_UNKNOWN:
        if((trace != TRACE_MUT_UNKNOWN) && (trace != TRACE_VAL_UNKNOWN))
          trace = TRACE_TAG_UNKNOWN;
        break;

      case TRACE_DYNAMIC:
      case TRACE_TUPLE:
        break;

      case TRACE_STATIC:
        // Can't happen here.
        pony_assert(0);
        break;
    }
  }

  return trace;
}
Example #7
0
static trace_t trace_type_union(ast_t* type)
{
  trace_t trace = TRACE_NONE;

  for(ast_t* child = ast_child(type);
    child != NULL;
    child = ast_sibling(child))
  {
    trace = trace_type_combine(trace, trace_type(child));
  }

  return trace;
}
Example #8
0
bool gentrace_needed(compile_t* c, ast_t* src_type, ast_t* dst_type)
{
  switch(trace_type(src_type))
  {
    case TRACE_NONE:
      pony_assert(0);
      return false;

    case TRACE_MACHINE_WORD:
    {
      if(ast_id(dst_type) == TK_NOMINAL)
      {
        ast_t* def = (ast_t*)ast_data(dst_type);
        if(ast_id(def) == TK_PRIMITIVE)
          return false;
      }
      return true;
    }

    case TRACE_PRIMITIVE:
      return false;

    case TRACE_TUPLE:
    {
      if(ast_id(dst_type) == TK_TUPLETYPE)
      {
        ast_t* src_child = ast_child(src_type);
        ast_t* dst_child = ast_child(dst_type);
        while((src_child != NULL) && (dst_child != NULL))
        {
          if(gentrace_needed(c, src_child, dst_child))
            return true;
          src_child = ast_sibling(src_child);
          dst_child = ast_sibling(dst_child);
        }
        pony_assert(src_child == NULL && dst_child == NULL);
      } else {
        // This is a boxed tuple. We'll have to trace the box anyway.
        return true;
      }

      return false;
    }

    default:
      break;
  }

  return true;
}
Example #9
0
static trace_t trace_type_isect(ast_t* type)
{
  trace_t trace = TRACE_DYNAMIC;

  for(ast_t* child = ast_child(type);
    child != NULL;
    child = ast_sibling(child))
  {
    trace_t t = trace_type(child);

    switch(t)
    {
      case TRACE_NONE:
      case TRACE_MAYBE: // Maybe, any refcap.
        // Can't be in an isect.
        assert(0);
        return TRACE_NONE;

      case TRACE_PRIMITIVE: // Primitive, any refcap.
      case TRACE_ACTOR: // Actor, tag.
      case TRACE_KNOWN_VAL:
      case TRACE_KNOWN: // Class or struct, not tag.
        return t;

      case TRACE_UNKNOWN_VAL:
      case TRACE_UNKNOWN: // Trait or interface, not tag.
      case TRACE_TAG: // Class or struct, tag.
      case TRACE_TAG_OR_ACTOR: // Trait or interface, tag.
        if(trace > t)
          trace = t;
        break;

      case TRACE_DYNAMIC:
      case TRACE_TUPLE:
        break;
    }
  }

  return trace;
}
Example #10
0
static void trace_dynamic_tuple(compile_t* c, LLVMValueRef ctx,
  LLVMValueRef ptr, LLVMValueRef desc, ast_t* type, ast_t* orig, ast_t* tuple)
{
  // Build a "don't care" type of our cardinality.
  size_t cardinality = ast_childcount(type);
  ast_t* dontcare = ast_from(type, TK_TUPLETYPE);

  for(size_t i = 0; i < cardinality; i++)
    ast_append(dontcare, ast_from(type, TK_DONTCARE));

  // Replace our type in the tuple type with the "don't care" type.
  bool in_tuple = (tuple != NULL);

  if(in_tuple)
    ast_swap(type, dontcare);
  else
    tuple = dontcare;

  // If the original type is a subtype of the test type, then we are always
  // the correct cardinality. Otherwise, we need to dynamically check
  // cardinality.
  LLVMBasicBlockRef is_true = codegen_block(c, "");
  LLVMBasicBlockRef is_false = codegen_block(c, "");

  if(!is_subtype(orig, tuple, NULL))
  {
    LLVMValueRef dynamic_count = gendesc_fieldcount(c, desc);
    LLVMValueRef static_count = LLVMConstInt(c->i32, cardinality, false);
    LLVMValueRef test = LLVMBuildICmp(c->builder, LLVMIntEQ, static_count,
      dynamic_count, "");

    // Skip if not the right cardinality.
    LLVMBuildCondBr(c->builder, test, is_true, is_false);
  } else {
    LLVMBuildBr(c->builder, is_true);
  }

  LLVMPositionBuilderAtEnd(c->builder, is_true);

  size_t index = 0;
  ast_t* child = ast_child(type);
  ast_t* dc_child = ast_child(dontcare);

  while(child != NULL)
  {
    switch(trace_type(child))
    {
      case TRACE_PRIMITIVE:
        // Skip this element.
        break;

      case TRACE_ACTOR:
      case TRACE_KNOWN:
      case TRACE_UNKNOWN:
      case TRACE_KNOWN_VAL:
      case TRACE_UNKNOWN_VAL:
      case TRACE_TAG:
      case TRACE_TAG_OR_ACTOR:
      case TRACE_DYNAMIC:
      {
        // If we are (A, B), turn (_, _) into (A, _).
        ast_t* swap = ast_dup(child);
        ast_swap(dc_child, swap);

        // Create a next block.
        LLVMBasicBlockRef next_block = codegen_block(c, "");

        // Load the object from the tuple field.
        LLVMValueRef field_info = gendesc_fieldinfo(c, desc, index);
        LLVMValueRef object = gendesc_fieldload(c, ptr, field_info);

        // Trace dynamic, even if the tuple thinks the field isn't dynamic.
        trace_dynamic(c, ctx, object, swap, orig, tuple, next_block);

        // Continue into the next block.
        LLVMBuildBr(c->builder, next_block);
        LLVMPositionBuilderAtEnd(c->builder, next_block);

        // Restore (A, _) to (_, _).
        ast_swap(swap, dc_child);
        ast_free_unattached(swap);
        break;
      }

      case TRACE_TUPLE:
      {
        // If we are (A, B), turn (_, _) into (A, _).
        ast_t* swap = ast_dup(child);
        ast_swap(dc_child, swap);

        // Get a pointer to the unboxed tuple and it's descriptor.
        LLVMValueRef field_info = gendesc_fieldinfo(c, desc, index);
        LLVMValueRef field_ptr = gendesc_fieldptr(c, ptr, field_info);
        LLVMValueRef field_desc = gendesc_fielddesc(c, field_info);

        // Trace the tuple dynamically.
        trace_dynamic_tuple(c, ctx, field_ptr, field_desc, swap, orig, tuple);

        // Restore (A, _) to (_, _).
        ast_swap(swap, dc_child);
        ast_free_unattached(swap);
        break;
      }

      default: {}
    }

    index++;
    child = ast_sibling(child);
    dc_child = ast_sibling(dc_child);
  }

  // Restore the tuple type.
  if(in_tuple)
    ast_swap(dontcare, type);

  ast_free_unattached(dontcare);

  // Continue with other possible tracings.
  LLVMBuildBr(c->builder, is_false);
  LLVMPositionBuilderAtEnd(c->builder, is_false);
}
Example #11
0
/*{
** Name: rdf_trace - Call RDF trace operation.
**
**	External call format:	status = rdf_trace(&db_debug_cb)
**
** Description:
**      This function is the standard entry point to RDF for setting and
**	clearing tracepoints(the "set trace point" command). Because RDF 
**	is a service facility, trace point for RDF can only be set on 
**	the server basis. There is no session level trace point. 
**	Db_debug_cb is the tracing control block that contains the trace 
**	flag information.
**
**	See file <rdftrace.h> for a description of all possible
**	RDF trace points.
**	
** Inputs:
**      debug_cb		    Pointer to a DB_DEBUG_CB
**        .db_trswitch              What operation to perform
**	    DB_TR_NOCHANGE	    None
**	    DB_TR_ON		    Turn on a tracepoint
**	    DB_TR_OFF		    Turn off a tracepoint
**	  .db_trace_point	    Trace point ID(the flag number)
**	  .db_value_count	    The number of values specified in
**				    the value array
**	  .db_vals[2]		    Optional values, to be interpreted
**				    differently for each tracepoint
** Outputs:
**	none
**
**	Returns:
**	    E_DB_OK			Function completed normally.
**	    E_DB_ERROR			Function failed due to error by caller;
**	    E_DB_FATAL			Function failed due to some internal
**					problem; 
**	Exceptions:
**		none
**
** Side Effects:
**	The trace vector in the server control block of RDF will be updated
**	to contain the trace information. The trace information will be persist
**	throughout the life of the server.
**
** History:
**	15-apr-86 (ac)
**          written
**	02-mar-87 (puree)
**	    replace global server control block with global pointer.
**	14-dec-1989 (teg)
**	    modify to go get svcb from SCF instead of using a global pointer
**	    to it.
**	16-jul-92 (teresa)
**	    prototypes
**	16-sep-92 (teresa)
**	    implement trace points rd3 and rd4 to clear ldbdesc cache.
**	22-apr-94 (teresa)
**	    added trace points rd11 and rd12 to dump memory statistics or all
**	    statistics.  This is an action trace point -- the dump occurs when 
**	    the trace point is selected rather than during query execution.
**	20-nov-2007 (thaju02)
**	    If trace point RD0022/RDU_CHECKSUM specified, invalidate 
**	    relcache. Entries need rdr_checksum to be calc/init'd 
**	    otherwise E_RD010D errors may be reported. (B119499)
*/
DB_STATUS
rdf_trace(DB_DEBUG_CB *debug_cb)
{
    i4		flag;
    i4		firstval;
    i4		secondval;
    DB_STATUS		status;
    i4		trace_scope;

    /* assure flag is legal */
    flag = debug_cb->db_trace_point;
    if (flag >= RDF_NB)
    {
	return (E_DB_ERROR);
    }

    /* There can be UP TO two values, but maybe they weren't given */
    firstval = (debug_cb->db_value_count > 0) ? debug_cb->db_vals[0] : 0L;
    secondval = (debug_cb->db_value_count > 1) ? debug_cb->db_vals[1] : 0L;

    /* see if this is an action trace.  Action traces require an immediate
    ** action rather than turning trace flags on/off/etc.
    */
    if ( (debug_cb->db_trswitch==DB_TR_ON) && (flag <= RD_ACT_MAX) )
    {
	/* see which action is requested.  Not all actions are implemented
	** yet, so its possible that this call may  become a no-opt 
	*/
	switch (flag)
	{
	case RD0001:
	case RD0002:
	case RD0003:
	case RD0004:
	case RD0005:
	case RD0010:
	    status=rdt_clear_cache(flag);
	    if (DB_FAILURE_MACRO(status))
	    {
		return(E_DB_ERROR);
	    }
	    break;
	case RD0011:
	    /* dump memory info.  This trace is used by tech support when
	    ** debugging out of memory errors. */
	    TRdisplay("\n...............................................\n");
	    TRdisplay("RDF Cache Memory Available:   %d\n",Rdi_svcb->rdv_memleft);
	    TRdisplay("RDF memory cache size     :   %d\n", 
			Rdi_svcb->rdv_pool_size);
	    TRdisplay("Max number of objects allowed on Cache:\n");
	    TRdisplay("   RELcache: %d,	    QTREE Cache: %d, \n",
			Rdi_svcb->rdv_cnt0_rdesc, Rdi_svcb->rdv_cnt1_qtree);
	    TRdisplay("   LDBDesc Cache %d,   DEFAULTS cache: %d\n",
			Rdi_svcb->rdv_cnt2_dist, Rdi_svcb->rdv_cnt3_default);
	    TRdisplay("Hashids:\n");
	    TRdisplay("   RELcache: %d,	    QTREE Cache: %d, \n",
			Rdi_svcb->rdv_rdesc_hashid, Rdi_svcb->rdv_qtree_hashid);
	    TRdisplay("   LDBDesc Cache %d,   DEFAULTS cache: %d\n",
			Rdi_svcb->rdv_dist_hashid, Rdi_svcb->rdv_def_hashid);
	    TRdisplay("...............................................\n");
	    break;
	case RD0012:
	    /* dump all of the RDF statistics */
	    rdf_report ( &Rdi_svcb->rdvstat );
	    break;

	    default:
		break;

	}
    }
    else
    {

	/*
	** determine if this is a session wide trace or a server wide trace
	** and process accordingly
	*/
	trace_scope = trace_type(flag);
	if (trace_scope == SVR_WIDE_TRACE)
	{   
	    /* turn trace on in svcb
	    **
	    ** Three possible actions: Turn on flag, turn it off, or do nothing.
	    */
	    switch (debug_cb->db_trswitch)
	    {
	    case DB_TR_ON:
		    if ((flag == RD0022) && 
			!(ult_check_macro(&Rdi_svcb->rdf_trace, 
					flag, &firstval, &secondval)))
		    {
			    /* setting RDU_CHECKSUM */
			    RDF_GLOBAL      global;
			    RDF_CB          rdfcb;
			    RDI_FCB         fcb;

			    global.rdfcb = &rdfcb;
			    rdfcb.rdf_rb.rdr_fcb = (PTR)&fcb;
			    rdfcb.rdf_info_blk = NULL;
			    rdfcb.rdf_rb.rdr_db_id = NULL;
			    rdfcb.rdf_rb.rdr_types_mask = 0;
			    rdfcb.rdf_rb.rdr_2types_mask = 0;
			    CSget_sid(&rdfcb.rdf_rb.rdr_session_id);
			    fcb.rdi_fac_id = DB_RDF_ID;
			    status = rdf_invalidate(&global, &rdfcb);
			    if (DB_FAILURE_MACRO(status))
				return(E_DB_ERROR);
		    }
		    ult_set_macro(&Rdi_svcb->rdf_trace, flag, firstval, secondval);
		break;

	    case DB_TR_OFF:
		    ult_clear_macro(&Rdi_svcb->rdf_trace, flag);
		break;

	    case DB_TR_NOCHANGE:
		/* Do nothing */
		break;

	    default:
		return (E_DB_ERROR);
	    };
	}
	else
	{
	    CS_SID	    sid;
	    RDF_SESS_CB	    *rdf_sess_cb;

	    /* 
	    ** this trace point is session specific, so use the session control
	    ** block for this trace point.
	    */

	    CSget_sid(&sid);
	    rdf_sess_cb = GET_RDF_SESSCB(sid);

	    /*
	    ** Three possible actions: Turn on flag, turn it off, or do nothing.
	    */

	    switch (debug_cb->db_trswitch)
	    {
	    case DB_TR_ON:
		    ult_set_macro(&rdf_sess_cb->rds_strace, 
				  flag, firstval, secondval);
		break;

	    case DB_TR_OFF:
		    ult_clear_macro(&rdf_sess_cb->rds_strace, flag);
		break;

	    case DB_TR_NOCHANGE:
		/* Do nothing */
		break;

	    default:
		return (E_DB_ERROR);
	    }
	}
    }
    return (E_DB_OK);
}
Example #12
0
void gentrace(compile_t* c, LLVMValueRef ctx, LLVMValueRef src_value,
  LLVMValueRef dst_value, ast_t* src_type, ast_t* dst_type)
{
  trace_t trace_method = trace_type(src_type);
  if(src_type != dst_type)
  {
    trace_method = trace_type_dst_cap(trace_method, trace_type(dst_type),
      src_type);
  }

  switch(trace_method)
  {
    case TRACE_NONE:
      pony_assert(0);
      return;

    case TRACE_MACHINE_WORD:
    {
      bool boxed = true;
      if(ast_id(dst_type) == TK_NOMINAL)
      {
        ast_t* def = (ast_t*)ast_data(dst_type);
        if(ast_id(def) == TK_PRIMITIVE)
          boxed = false;
      }

      if(boxed)
        trace_known(c, ctx, dst_value, src_type, PONY_TRACE_IMMUTABLE);

      return;
    }

    case TRACE_PRIMITIVE:
      return;

    case TRACE_MAYBE:
      trace_maybe(c, ctx, dst_value, src_type);
      return;

    case TRACE_VAL_KNOWN:
      trace_known(c, ctx, dst_value, src_type, PONY_TRACE_IMMUTABLE);
      return;

    case TRACE_VAL_UNKNOWN:
      trace_unknown(c, ctx, dst_value, PONY_TRACE_IMMUTABLE);
      return;

    case TRACE_MUT_KNOWN:
      trace_known(c, ctx, dst_value, src_type, PONY_TRACE_MUTABLE);
      return;

    case TRACE_MUT_UNKNOWN:
      trace_unknown(c, ctx, dst_value, PONY_TRACE_MUTABLE);
      return;

    case TRACE_TAG_KNOWN:
      trace_known(c, ctx, dst_value, src_type, PONY_TRACE_OPAQUE);
      return;

    case TRACE_TAG_UNKNOWN:
      trace_unknown(c, ctx, dst_value, PONY_TRACE_OPAQUE);
      return;

    case TRACE_STATIC:
      trace_static(c, ctx, dst_value, src_type, dst_type);
      return;

    case TRACE_DYNAMIC:
    {
      LLVMBasicBlockRef next_block = codegen_block(c, "");
      trace_dynamic(c, ctx, dst_value, src_type, dst_type, NULL, next_block);
      LLVMBuildBr(c->builder, next_block);
      LLVMPositionBuilderAtEnd(c->builder, next_block);
      return;
    }

    case TRACE_TUPLE:
      trace_tuple(c, ctx, src_value, dst_value, src_type, dst_type);
      return;
  }
}