📄 dt_cg.c
字号:
/* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only. * See the file usr/src/LICENSING.NOTICE in this distribution or * http://www.opensolaris.org/license/ for details. */#pragma ident "@(#)dt_cg.c 1.9 04/12/27 SMI"#include <sys/types.h>#include <sys/sysmacros.h>#include <sys/isa_defs.h>#include <strings.h>#include <stdlib.h>#include <setjmp.h>#include <assert.h>#include <errno.h>#include <dt_impl.h>#include <dt_grammar.h>#include <dt_parser.h>static void dt_cg_node(dt_node_t *, dt_irlist_t *, dt_regset_t *);static dt_irnode_t *dt_cg_node_alloc(uint_t label, dif_instr_t instr){ dt_irnode_t *dip = malloc(sizeof (dt_irnode_t)); if (dip == NULL) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); dip->di_label = label; dip->di_instr = instr; dip->di_ident = NULL; dip->di_next = NULL; return (dip);}/* * Code generator wrapper function for ctf_member_info. If we are given a * reference to a forward declaration tag, search the entire type space for * the actual definition and then call ctf_member_info on the result. */static ctf_file_t *dt_cg_membinfo(ctf_file_t *fp, ctf_id_t type, const char *s, ctf_membinfo_t *mp){ while (ctf_type_kind(fp, type) == CTF_K_FORWARD) { char n[DT_TYPE_NAMELEN]; dtrace_typeinfo_t dtt; if (ctf_type_name(fp, type, n, sizeof (n)) == NULL || dt_type_lookup(n, &dtt) == -1 || ( dtt.dtt_ctfp == fp && dtt.dtt_type == type)) break; /* unable to improve our position */ fp = dtt.dtt_ctfp; type = ctf_type_resolve(fp, dtt.dtt_type); } if (ctf_member_info(fp, type, s, mp) == CTF_ERR) return (NULL); /* ctf_errno is set for us */ return (fp);}static voiddt_cg_xsetx(dt_irlist_t *dlp, dt_ident_t *idp, uint_t lbl, int reg, uint64_t x){ int flag = idp != NULL ? DT_INT_PRIVATE : DT_INT_SHARED; int intoff = dt_inttab_insert(yypcb->pcb_inttab, x, flag); dif_instr_t instr = DIF_INSTR_SETX((uint_t)intoff, reg); if (intoff == -1) longjmp(yypcb->pcb_jmpbuf, EDT_NOMEM); if (intoff > DIF_INTOFF_MAX) longjmp(yypcb->pcb_jmpbuf, EDT_INT2BIG); dt_irlist_append(dlp, dt_cg_node_alloc(lbl, instr)); if (idp != NULL) dlp->dl_last->di_ident = idp;}static voiddt_cg_setx(dt_irlist_t *dlp, int reg, uint64_t x){ dt_cg_xsetx(dlp, NULL, DT_LBL_NONE, reg, x);}/* * When loading bit-fields, we want to convert a byte count in the range * 1-8 to the closest power of 2 (e.g. 3->4, 5->8, etc). The clp2() function * is a clever implementation from "Hacker's Delight" by Henry Warren, Jr. */static size_tclp2(size_t x){ x--; x |= (x >> 1); x |= (x >> 2); x |= (x >> 4); x |= (x >> 8); x |= (x >> 16); return (x + 1);}/* * Lookup the correct load opcode to use for the specified node and CTF type. * We determine the size and convert it to a 3-bit index. Our lookup table * is constructed to use a 5-bit index, consisting of the 3-bit size 0-7, a * bit for the sign, and a bit for userland address. For example, a 4-byte * signed load from userland would be at the following table index: * user=1 sign=1 size=4 => binary index 11011 = decimal index 27 */static uint_tdt_cg_load(dt_node_t *dnp, ctf_file_t *ctfp, ctf_id_t type){ static const uint_t ops[] = { DIF_OP_LDUB, DIF_OP_LDUH, 0, DIF_OP_LDUW, 0, 0, 0, DIF_OP_LDX, DIF_OP_LDSB, DIF_OP_LDSH, 0, DIF_OP_LDSW, 0, 0, 0, DIF_OP_LDX, DIF_OP_ULDUB, DIF_OP_ULDUH, 0, DIF_OP_ULDUW, 0, 0, 0, DIF_OP_ULDX, DIF_OP_ULDSB, DIF_OP_ULDSH, 0, DIF_OP_ULDSW, 0, 0, 0, DIF_OP_ULDX, }; ctf_encoding_t e; ssize_t size; /* * If we're loading a bit-field, the size of our load is found by * rounding cte_bits up to a byte boundary and then finding the * nearest power of two to this value (see clp2(), above). */ if ((dnp->dn_flags & DT_NF_BITFIELD) && ctf_type_encoding(ctfp, type, &e) != CTF_ERR) size = clp2(P2ROUNDUP(e.cte_bits, NBBY) / NBBY); else size = ctf_type_size(ctfp, type); if (size < 1 || size > 8 || (size & (size - 1)) != 0) { xyerror(D_UNKNOWN, "internal error -- cg cannot load " "size %ld when passed by value\n", (long)size); } size--; /* convert size to 3-bit index */ if (dnp->dn_flags & DT_NF_SIGNED) size |= 0x08; if (dnp->dn_flags & DT_NF_USERLAND) size |= 0x10; return (ops[size]);}static voiddt_cg_ptrsize(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp, uint_t op, int dreg){ ctf_file_t *ctfp = dnp->dn_ctfp; ctf_arinfo_t r; dif_instr_t instr; ctf_id_t type; uint_t kind; ssize_t size; int sreg; if ((sreg = dt_regset_alloc(drp)) == -1) longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); type = ctf_type_resolve(ctfp, dnp->dn_type); kind = ctf_type_kind(ctfp, type); assert(kind == CTF_K_POINTER || kind == CTF_K_ARRAY); if (kind == CTF_K_ARRAY) { if (ctf_array_info(ctfp, type, &r) != 0) { yypcb->pcb_hdl->dt_ctferr = ctf_errno(ctfp); longjmp(yypcb->pcb_jmpbuf, EDT_CTF); } type = r.ctr_contents; } else type = ctf_type_reference(ctfp, type); if ((size = ctf_type_size(ctfp, type)) == 1) return; /* multiply or divide by one can be omitted */ dt_cg_setx(dlp, sreg, size); instr = DIF_INSTR_FMT(op, dreg, sreg, dreg); dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); dt_regset_free(drp, sreg);}/* * If the result of a "." or "->" operation is a bit-field, we use this routine * to generate an epilogue to the load instruction that extracts the value. In * the diagrams below the "ld??" is the load instruction that is generated to * load the containing word that is generating prior to calling this function. * * Epilogue for unsigned fields: Epilogue for signed fields: * * ldu? [r1], r1 lds? [r1], r1 * setx USHIFT, r2 setx 64 - SSHIFT, r2 * srl r1, r2, r1 sll r1, r2, r1 * setx (1 << bits) - 1, r2 setx 64 - bits, r2 * and r1, r2, r1 sra r1, r2, r1 * * The *SHIFT constants above changes value depending on the endian-ness of our * target architecture. Refer to the comments below for more details. */static voiddt_cg_field_get(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp, ctf_file_t *fp, const ctf_membinfo_t *mp){ ctf_encoding_t e; dif_instr_t instr; uint64_t shift; int r1, r2; if (ctf_type_encoding(fp, mp->ctm_type, &e) != 0 || e.cte_bits > 64) { xyerror(D_UNKNOWN, "cg: bad field: off %lu type <%ld> " "bits %u\n", mp->ctm_offset, mp->ctm_type, e.cte_bits); } assert(dnp->dn_op == DT_TOK_PTR || dnp->dn_op == DT_TOK_DOT); r1 = dnp->dn_left->dn_reg; if ((r2 = dt_regset_alloc(drp)) == -1) longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); /* * On little-endian architectures, ctm_offset counts from the right so * ctm_offset % NBBY itself is the amount we want to shift right to * move the value bits to the little end of the register to mask them. * On big-endian architectures, ctm_offset counts from the left so we * must subtract (ctm_offset % NBBY + cte_bits) from the size in bits * we used for the load. The size of our load in turn is found by * rounding cte_bits up to a byte boundary and then finding the * nearest power of two to this value (see clp2(), above). These * properties are used to compute shift as USHIFT or SSHIFT, below. */ if (dnp->dn_flags & DT_NF_SIGNED) {#ifdef _BIG_ENDIAN shift = clp2(P2ROUNDUP(e.cte_bits, NBBY) / NBBY) * NBBY - mp->ctm_offset % NBBY;#else shift = mp->ctm_offset % NBBY + e.cte_bits;#endif dt_cg_setx(dlp, r2, 64 - shift); instr = DIF_INSTR_FMT(DIF_OP_SLL, r1, r2, r1); dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); dt_cg_setx(dlp, r2, 64 - e.cte_bits); instr = DIF_INSTR_FMT(DIF_OP_SRA, r1, r2, r1); dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); } else {#ifdef _BIG_ENDIAN shift = clp2(P2ROUNDUP(e.cte_bits, NBBY) / NBBY) * NBBY - (mp->ctm_offset % NBBY + e.cte_bits);#else shift = mp->ctm_offset % NBBY;#endif dt_cg_setx(dlp, r2, shift); instr = DIF_INSTR_FMT(DIF_OP_SRL, r1, r2, r1); dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); dt_cg_setx(dlp, r2, (1ULL << e.cte_bits) - 1); instr = DIF_INSTR_FMT(DIF_OP_AND, r1, r2, r1); dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); } dt_regset_free(drp, r2);}/* * If the destination of a store operation is a bit-field, we use this routine * to generate a prologue to the store instruction that loads the surrounding * bits, clears the destination field, and ORs in the new value of the field. * In the diagram below the "st?" is the store instruction that is generated to * store the containing word that is generating after calling this function. * * ld [dst->dn_reg], r1 * setx ~(((1 << cte_bits) - 1) << (ctm_offset % NBBY)), r2 * and r1, r2, r1 * * setx (1 << cte_bits) - 1, r2 * and src->dn_reg, r2, r2 * setx ctm_offset % NBBY, r3 * sll r2, r3, r2 * * or r1, r2, r1 * st? r1, [dst->dn_reg] * * This routine allocates a new register to hold the value to be stored and * returns it. The caller is responsible for freeing this register later. */static intdt_cg_field_set(dt_node_t *src, dt_irlist_t *dlp, dt_regset_t *drp, dt_node_t *dst){ uint64_t cmask, fmask, shift; dif_instr_t instr; int r1, r2, r3; ctf_membinfo_t m; ctf_encoding_t e; ctf_file_t *fp, *ofp; ctf_id_t type; assert(dst->dn_op == DT_TOK_PTR || dst->dn_op == DT_TOK_DOT); assert(dst->dn_right->dn_kind == DT_NODE_IDENT); fp = dst->dn_left->dn_ctfp; type = ctf_type_resolve(fp, dst->dn_left->dn_type); if (dst->dn_op == DT_TOK_PTR) { type = ctf_type_reference(fp, type); type = ctf_type_resolve(fp, type); } if ((fp = dt_cg_membinfo(ofp = fp, type, dst->dn_right->dn_string, &m)) == NULL) { yypcb->pcb_hdl->dt_ctferr = ctf_errno(ofp); longjmp(yypcb->pcb_jmpbuf, EDT_CTF); } if (ctf_type_encoding(fp, m.ctm_type, &e) != 0 || e.cte_bits > 64) { xyerror(D_UNKNOWN, "cg: bad field: off %lu type <%ld> " "bits %u\n", m.ctm_offset, m.ctm_type, e.cte_bits); } if ((r1 = dt_regset_alloc(drp)) == -1 || (r2 = dt_regset_alloc(drp)) == -1 || (r3 = dt_regset_alloc(drp)) == -1) longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); /* * Compute shifts and masks. We need to compute "shift" as the amount * we need to shift left to position our field in the containing word. * Refer to the comments in dt_cg_field_get(), above, for more info. * We then compute fmask as the mask that truncates the value in the * input register to width cte_bits, and cmask as the mask used to * pass through the containing bits and zero the field bits. */#ifdef _BIG_ENDIAN shift = clp2(P2ROUNDUP(e.cte_bits, NBBY) / NBBY) * NBBY - (m.ctm_offset % NBBY + e.cte_bits);#else shift = m.ctm_offset % NBBY;#endif fmask = (1ULL << e.cte_bits) - 1; cmask = ~(fmask << shift); instr = DIF_INSTR_LOAD( dt_cg_load(dst, fp, m.ctm_type), dst->dn_reg, r1); dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); dt_cg_setx(dlp, r2, cmask); instr = DIF_INSTR_FMT(DIF_OP_AND, r1, r2, r1); dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); dt_cg_setx(dlp, r2, fmask); instr = DIF_INSTR_FMT(DIF_OP_AND, src->dn_reg, r2, r2); dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); dt_cg_setx(dlp, r3, shift); instr = DIF_INSTR_FMT(DIF_OP_SLL, r2, r3, r2); dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); instr = DIF_INSTR_FMT(DIF_OP_OR, r1, r2, r1); dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); dt_regset_free(drp, r3); dt_regset_free(drp, r2); return (r1);}static voiddt_cg_store(dt_node_t *src, dt_irlist_t *dlp, dt_regset_t *drp, dt_node_t *dst){ ctf_encoding_t e; dif_instr_t instr; size_t size; int reg; /* * If we're loading a bit-field, the size of our store is found by * rounding dst's cte_bits up to a byte boundary and then finding the * nearest power of two to this value (see clp2(), above). */ if ((dst->dn_flags & DT_NF_BITFIELD) && ctf_type_encoding(dst->dn_ctfp, dst->dn_type, &e) != CTF_ERR) size = clp2(P2ROUNDUP(e.cte_bits, NBBY) / NBBY); else size = dt_node_type_size(src); if (src->dn_flags & DT_NF_REF) { if ((reg = dt_regset_alloc(drp)) == -1) longjmp(yypcb->pcb_jmpbuf, EDT_NOREG); dt_cg_setx(dlp, reg, size); instr = DIF_INSTR_COPYS(src->dn_reg, reg, dst->dn_reg); dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); dt_regset_free(drp, reg); } else { if (dst->dn_flags & DT_NF_BITFIELD) reg = dt_cg_field_set(src, dlp, drp, dst); else reg = src->dn_reg; switch (size) { case 1: instr = DIF_INSTR_STORE(DIF_OP_STB, reg, dst->dn_reg); break; case 2: instr = DIF_INSTR_STORE(DIF_OP_STH, reg, dst->dn_reg); break; case 4: instr = DIF_INSTR_STORE(DIF_OP_STW, reg, dst->dn_reg); break; case 8: instr = DIF_INSTR_STORE(DIF_OP_STX, reg, dst->dn_reg); break; default: xyerror(D_UNKNOWN, "internal error -- cg cannot store " "size %lu when passed by value\n", (ulong_t)size); } dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr)); if (dst->dn_flags & DT_NF_BITFIELD) dt_regset_free(drp, reg); }}/* * Generate code for a typecast or for argument promotion from the type of the * actual to the type of the formal. We need to generate code for casts when * a scalar type is being narrowed or changing signed-ness. We first shift the * desired bits high (losing excess bits if narrowing) and then shift them down * using logical shift (unsigned result) or arithmetic shift (signed result). */static voiddt_cg_typecast(const dt_node_t *src, const dt_node_t *dst, dt_irlist_t *dlp, dt_regset_t *drp){ size_t srcsize = dt_node_type_size(src); size_t dstsize = dt_node_type_size(dst); dif_instr_t instr; int reg, n; if (dt_node_is_scalar(dst) && (dstsize < srcsize || (src->dn_flags & DT_NF_SIGNED) ^ (dst->dn_flags & DT_NF_SIGNED))) { if ((reg = dt_regset_alloc(drp)) == -1)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -