Annotation of qemu/tcg/optimize.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Optimizations for Tiny Code Generator for QEMU
        !             3:  *
        !             4:  * Copyright (c) 2010 Samsung Electronics.
        !             5:  * Contributed by Kirill Batuzov <[email protected]>
        !             6:  *
        !             7:  * Permission is hereby granted, free of charge, to any person obtaining a copy
        !             8:  * of this software and associated documentation files (the "Software"), to deal
        !             9:  * in the Software without restriction, including without limitation the rights
        !            10:  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        !            11:  * copies of the Software, and to permit persons to whom the Software is
        !            12:  * furnished to do so, subject to the following conditions:
        !            13:  *
        !            14:  * The above copyright notice and this permission notice shall be included in
        !            15:  * all copies or substantial portions of the Software.
        !            16:  *
        !            17:  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        !            18:  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        !            19:  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
        !            20:  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        !            21:  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        !            22:  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
        !            23:  * THE SOFTWARE.
        !            24:  */
        !            25: 
        !            26: #include "config.h"
        !            27: 
        !            28: #include <stdlib.h>
        !            29: #include <stdio.h>
        !            30: 
        !            31: #include "qemu-common.h"
        !            32: #include "tcg-op.h"
        !            33: 
        !            34: #define CASE_OP_32_64(x)                        \
        !            35:         glue(glue(case INDEX_op_, x), _i32):    \
        !            36:         glue(glue(case INDEX_op_, x), _i64)
        !            37: 
        !            38: typedef enum {
        !            39:     TCG_TEMP_UNDEF = 0,
        !            40:     TCG_TEMP_CONST,
        !            41:     TCG_TEMP_COPY,
        !            42:     TCG_TEMP_HAS_COPY,
        !            43:     TCG_TEMP_ANY
        !            44: } tcg_temp_state;
        !            45: 
        !            46: struct tcg_temp_info {
        !            47:     tcg_temp_state state;
        !            48:     uint16_t prev_copy;
        !            49:     uint16_t next_copy;
        !            50:     tcg_target_ulong val;
        !            51: };
        !            52: 
        !            53: static struct tcg_temp_info temps[TCG_MAX_TEMPS];
        !            54: 
        !            55: /* Reset TEMP's state to TCG_TEMP_ANY.  If TEMP was a representative of some
        !            56:    class of equivalent temp's, a new representative should be chosen in this
        !            57:    class. */
        !            58: static void reset_temp(TCGArg temp, int nb_temps, int nb_globals)
        !            59: {
        !            60:     int i;
        !            61:     TCGArg new_base = (TCGArg)-1;
        !            62:     if (temps[temp].state == TCG_TEMP_HAS_COPY) {
        !            63:         for (i = temps[temp].next_copy; i != temp; i = temps[i].next_copy) {
        !            64:             if (i >= nb_globals) {
        !            65:                 temps[i].state = TCG_TEMP_HAS_COPY;
        !            66:                 new_base = i;
        !            67:                 break;
        !            68:             }
        !            69:         }
        !            70:         for (i = temps[temp].next_copy; i != temp; i = temps[i].next_copy) {
        !            71:             if (new_base == (TCGArg)-1) {
        !            72:                 temps[i].state = TCG_TEMP_ANY;
        !            73:             } else {
        !            74:                 temps[i].val = new_base;
        !            75:             }
        !            76:         }
        !            77:         temps[temps[temp].next_copy].prev_copy = temps[temp].prev_copy;
        !            78:         temps[temps[temp].prev_copy].next_copy = temps[temp].next_copy;
        !            79:     } else if (temps[temp].state == TCG_TEMP_COPY) {
        !            80:         temps[temps[temp].next_copy].prev_copy = temps[temp].prev_copy;
        !            81:         temps[temps[temp].prev_copy].next_copy = temps[temp].next_copy;
        !            82:         new_base = temps[temp].val;
        !            83:     }
        !            84:     temps[temp].state = TCG_TEMP_ANY;
        !            85:     if (new_base != (TCGArg)-1 && temps[new_base].next_copy == new_base) {
        !            86:         temps[new_base].state = TCG_TEMP_ANY;
        !            87:     }
        !            88: }
        !            89: 
        !            90: static int op_bits(TCGOpcode op)
        !            91: {
        !            92:     const TCGOpDef *def = &tcg_op_defs[op];
        !            93:     return def->flags & TCG_OPF_64BIT ? 64 : 32;
        !            94: }
        !            95: 
        !            96: static TCGOpcode op_to_movi(TCGOpcode op)
        !            97: {
        !            98:     switch (op_bits(op)) {
        !            99:     case 32:
        !           100:         return INDEX_op_movi_i32;
        !           101:     case 64:
        !           102:         return INDEX_op_movi_i64;
        !           103:     default:
        !           104:         fprintf(stderr, "op_to_movi: unexpected return value of "
        !           105:                 "function op_bits.\n");
        !           106:         tcg_abort();
        !           107:     }
        !           108: }
        !           109: 
        !           110: static void tcg_opt_gen_mov(TCGContext *s, TCGArg *gen_args, TCGArg dst,
        !           111:                             TCGArg src, int nb_temps, int nb_globals)
        !           112: {
        !           113:         reset_temp(dst, nb_temps, nb_globals);
        !           114:         assert(temps[src].state != TCG_TEMP_COPY);
        !           115:         /* Don't try to copy if one of temps is a global or either one
        !           116:            is local and another is register */
        !           117:         if (src >= nb_globals && dst >= nb_globals &&
        !           118:             tcg_arg_is_local(s, src) == tcg_arg_is_local(s, dst)) {
        !           119:             assert(temps[src].state != TCG_TEMP_CONST);
        !           120:             if (temps[src].state != TCG_TEMP_HAS_COPY) {
        !           121:                 temps[src].state = TCG_TEMP_HAS_COPY;
        !           122:                 temps[src].next_copy = src;
        !           123:                 temps[src].prev_copy = src;
        !           124:             }
        !           125:             temps[dst].state = TCG_TEMP_COPY;
        !           126:             temps[dst].val = src;
        !           127:             temps[dst].next_copy = temps[src].next_copy;
        !           128:             temps[dst].prev_copy = src;
        !           129:             temps[temps[dst].next_copy].prev_copy = dst;
        !           130:             temps[src].next_copy = dst;
        !           131:         }
        !           132:         gen_args[0] = dst;
        !           133:         gen_args[1] = src;
        !           134: }
        !           135: 
        !           136: static void tcg_opt_gen_movi(TCGArg *gen_args, TCGArg dst, TCGArg val,
        !           137:                              int nb_temps, int nb_globals)
        !           138: {
        !           139:         reset_temp(dst, nb_temps, nb_globals);
        !           140:         temps[dst].state = TCG_TEMP_CONST;
        !           141:         temps[dst].val = val;
        !           142:         gen_args[0] = dst;
        !           143:         gen_args[1] = val;
        !           144: }
        !           145: 
        !           146: static TCGOpcode op_to_mov(TCGOpcode op)
        !           147: {
        !           148:     switch (op_bits(op)) {
        !           149:     case 32:
        !           150:         return INDEX_op_mov_i32;
        !           151:     case 64:
        !           152:         return INDEX_op_mov_i64;
        !           153:     default:
        !           154:         fprintf(stderr, "op_to_mov: unexpected return value of "
        !           155:                 "function op_bits.\n");
        !           156:         tcg_abort();
        !           157:     }
        !           158: }
        !           159: 
        !           160: static TCGArg do_constant_folding_2(TCGOpcode op, TCGArg x, TCGArg y)
        !           161: {
        !           162:     switch (op) {
        !           163:     CASE_OP_32_64(add):
        !           164:         return x + y;
        !           165: 
        !           166:     CASE_OP_32_64(sub):
        !           167:         return x - y;
        !           168: 
        !           169:     CASE_OP_32_64(mul):
        !           170:         return x * y;
        !           171: 
        !           172:     CASE_OP_32_64(and):
        !           173:         return x & y;
        !           174: 
        !           175:     CASE_OP_32_64(or):
        !           176:         return x | y;
        !           177: 
        !           178:     CASE_OP_32_64(xor):
        !           179:         return x ^ y;
        !           180: 
        !           181:     case INDEX_op_shl_i32:
        !           182:         return (uint32_t)x << (uint32_t)y;
        !           183: 
        !           184:     case INDEX_op_shl_i64:
        !           185:         return (uint64_t)x << (uint64_t)y;
        !           186: 
        !           187:     case INDEX_op_shr_i32:
        !           188:         return (uint32_t)x >> (uint32_t)y;
        !           189: 
        !           190:     case INDEX_op_shr_i64:
        !           191:         return (uint64_t)x >> (uint64_t)y;
        !           192: 
        !           193:     case INDEX_op_sar_i32:
        !           194:         return (int32_t)x >> (int32_t)y;
        !           195: 
        !           196:     case INDEX_op_sar_i64:
        !           197:         return (int64_t)x >> (int64_t)y;
        !           198: 
        !           199:     case INDEX_op_rotr_i32:
        !           200:         x = ((uint32_t)x << (32 - y)) | ((uint32_t)x >> y);
        !           201:         return x;
        !           202: 
        !           203:     case INDEX_op_rotr_i64:
        !           204:         x = ((uint64_t)x << (64 - y)) | ((uint64_t)x >> y);
        !           205:         return x;
        !           206: 
        !           207:     case INDEX_op_rotl_i32:
        !           208:         x = ((uint32_t)x << y) | ((uint32_t)x >> (32 - y));
        !           209:         return x;
        !           210: 
        !           211:     case INDEX_op_rotl_i64:
        !           212:         x = ((uint64_t)x << y) | ((uint64_t)x >> (64 - y));
        !           213:         return x;
        !           214: 
        !           215:     CASE_OP_32_64(not):
        !           216:         return ~x;
        !           217: 
        !           218:     CASE_OP_32_64(neg):
        !           219:         return -x;
        !           220: 
        !           221:     CASE_OP_32_64(andc):
        !           222:         return x & ~y;
        !           223: 
        !           224:     CASE_OP_32_64(orc):
        !           225:         return x | ~y;
        !           226: 
        !           227:     CASE_OP_32_64(eqv):
        !           228:         return ~(x ^ y);
        !           229: 
        !           230:     CASE_OP_32_64(nand):
        !           231:         return ~(x & y);
        !           232: 
        !           233:     CASE_OP_32_64(nor):
        !           234:         return ~(x | y);
        !           235: 
        !           236:     CASE_OP_32_64(ext8s):
        !           237:         return (int8_t)x;
        !           238: 
        !           239:     CASE_OP_32_64(ext16s):
        !           240:         return (int16_t)x;
        !           241: 
        !           242:     CASE_OP_32_64(ext8u):
        !           243:         return (uint8_t)x;
        !           244: 
        !           245:     CASE_OP_32_64(ext16u):
        !           246:         return (uint16_t)x;
        !           247: 
        !           248:     case INDEX_op_ext32s_i64:
        !           249:         return (int32_t)x;
        !           250: 
        !           251:     case INDEX_op_ext32u_i64:
        !           252:         return (uint32_t)x;
        !           253: 
        !           254:     default:
        !           255:         fprintf(stderr,
        !           256:                 "Unrecognized operation %d in do_constant_folding.\n", op);
        !           257:         tcg_abort();
        !           258:     }
        !           259: }
        !           260: 
        !           261: static TCGArg do_constant_folding(TCGOpcode op, TCGArg x, TCGArg y)
        !           262: {
        !           263:     TCGArg res = do_constant_folding_2(op, x, y);
        !           264:     if (op_bits(op) == 32) {
        !           265:         res &= 0xffffffff;
        !           266:     }
        !           267:     return res;
        !           268: }
        !           269: 
        !           270: /* Propagate constants and copies, fold constant expressions. */
        !           271: static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr,
        !           272:                                     TCGArg *args, TCGOpDef *tcg_op_defs)
        !           273: {
        !           274:     int i, nb_ops, op_index, nb_temps, nb_globals, nb_call_args;
        !           275:     TCGOpcode op;
        !           276:     const TCGOpDef *def;
        !           277:     TCGArg *gen_args;
        !           278:     TCGArg tmp;
        !           279:     /* Array VALS has an element for each temp.
        !           280:        If this temp holds a constant then its value is kept in VALS' element.
        !           281:        If this temp is a copy of other ones then this equivalence class'
        !           282:        representative is kept in VALS' element.
        !           283:        If this temp is neither copy nor constant then corresponding VALS'
        !           284:        element is unused. */
        !           285: 
        !           286:     nb_temps = s->nb_temps;
        !           287:     nb_globals = s->nb_globals;
        !           288:     memset(temps, 0, nb_temps * sizeof(struct tcg_temp_info));
        !           289: 
        !           290:     nb_ops = tcg_opc_ptr - gen_opc_buf;
        !           291:     gen_args = args;
        !           292:     for (op_index = 0; op_index < nb_ops; op_index++) {
        !           293:         op = gen_opc_buf[op_index];
        !           294:         def = &tcg_op_defs[op];
        !           295:         /* Do copy propagation */
        !           296:         if (!(def->flags & (TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS))) {
        !           297:             assert(op != INDEX_op_call);
        !           298:             for (i = def->nb_oargs; i < def->nb_oargs + def->nb_iargs; i++) {
        !           299:                 if (temps[args[i]].state == TCG_TEMP_COPY) {
        !           300:                     args[i] = temps[args[i]].val;
        !           301:                 }
        !           302:             }
        !           303:         }
        !           304: 
        !           305:         /* For commutative operations make constant second argument */
        !           306:         switch (op) {
        !           307:         CASE_OP_32_64(add):
        !           308:         CASE_OP_32_64(mul):
        !           309:         CASE_OP_32_64(and):
        !           310:         CASE_OP_32_64(or):
        !           311:         CASE_OP_32_64(xor):
        !           312:         CASE_OP_32_64(eqv):
        !           313:         CASE_OP_32_64(nand):
        !           314:         CASE_OP_32_64(nor):
        !           315:             if (temps[args[1]].state == TCG_TEMP_CONST) {
        !           316:                 tmp = args[1];
        !           317:                 args[1] = args[2];
        !           318:                 args[2] = tmp;
        !           319:             }
        !           320:             break;
        !           321:         default:
        !           322:             break;
        !           323:         }
        !           324: 
        !           325:         /* Simplify expression if possible. */
        !           326:         switch (op) {
        !           327:         CASE_OP_32_64(add):
        !           328:         CASE_OP_32_64(sub):
        !           329:         CASE_OP_32_64(shl):
        !           330:         CASE_OP_32_64(shr):
        !           331:         CASE_OP_32_64(sar):
        !           332:         CASE_OP_32_64(rotl):
        !           333:         CASE_OP_32_64(rotr):
        !           334:             if (temps[args[1]].state == TCG_TEMP_CONST) {
        !           335:                 /* Proceed with possible constant folding. */
        !           336:                 break;
        !           337:             }
        !           338:             if (temps[args[2]].state == TCG_TEMP_CONST
        !           339:                 && temps[args[2]].val == 0) {
        !           340:                 if ((temps[args[0]].state == TCG_TEMP_COPY
        !           341:                     && temps[args[0]].val == args[1])
        !           342:                     || args[0] == args[1]) {
        !           343:                     args += 3;
        !           344:                     gen_opc_buf[op_index] = INDEX_op_nop;
        !           345:                 } else {
        !           346:                     gen_opc_buf[op_index] = op_to_mov(op);
        !           347:                     tcg_opt_gen_mov(s, gen_args, args[0], args[1],
        !           348:                                     nb_temps, nb_globals);
        !           349:                     gen_args += 2;
        !           350:                     args += 3;
        !           351:                 }
        !           352:                 continue;
        !           353:             }
        !           354:             break;
        !           355:         CASE_OP_32_64(mul):
        !           356:             if ((temps[args[2]].state == TCG_TEMP_CONST
        !           357:                 && temps[args[2]].val == 0)) {
        !           358:                 gen_opc_buf[op_index] = op_to_movi(op);
        !           359:                 tcg_opt_gen_movi(gen_args, args[0], 0, nb_temps, nb_globals);
        !           360:                 args += 3;
        !           361:                 gen_args += 2;
        !           362:                 continue;
        !           363:             }
        !           364:             break;
        !           365:         CASE_OP_32_64(or):
        !           366:         CASE_OP_32_64(and):
        !           367:             if (args[1] == args[2]) {
        !           368:                 if (args[1] == args[0]) {
        !           369:                     args += 3;
        !           370:                     gen_opc_buf[op_index] = INDEX_op_nop;
        !           371:                 } else {
        !           372:                     gen_opc_buf[op_index] = op_to_mov(op);
        !           373:                     tcg_opt_gen_mov(s, gen_args, args[0], args[1], nb_temps,
        !           374:                                     nb_globals);
        !           375:                     gen_args += 2;
        !           376:                     args += 3;
        !           377:                 }
        !           378:                 continue;
        !           379:             }
        !           380:             break;
        !           381:         default:
        !           382:             break;
        !           383:         }
        !           384: 
        !           385:         /* Propagate constants through copy operations and do constant
        !           386:            folding.  Constants will be substituted to arguments by register
        !           387:            allocator where needed and possible.  Also detect copies. */
        !           388:         switch (op) {
        !           389:         CASE_OP_32_64(mov):
        !           390:             if ((temps[args[1]].state == TCG_TEMP_COPY
        !           391:                 && temps[args[1]].val == args[0])
        !           392:                 || args[0] == args[1]) {
        !           393:                 args += 2;
        !           394:                 gen_opc_buf[op_index] = INDEX_op_nop;
        !           395:                 break;
        !           396:             }
        !           397:             if (temps[args[1]].state != TCG_TEMP_CONST) {
        !           398:                 tcg_opt_gen_mov(s, gen_args, args[0], args[1],
        !           399:                                 nb_temps, nb_globals);
        !           400:                 gen_args += 2;
        !           401:                 args += 2;
        !           402:                 break;
        !           403:             }
        !           404:             /* Source argument is constant.  Rewrite the operation and
        !           405:                let movi case handle it. */
        !           406:             op = op_to_movi(op);
        !           407:             gen_opc_buf[op_index] = op;
        !           408:             args[1] = temps[args[1]].val;
        !           409:             /* fallthrough */
        !           410:         CASE_OP_32_64(movi):
        !           411:             tcg_opt_gen_movi(gen_args, args[0], args[1], nb_temps, nb_globals);
        !           412:             gen_args += 2;
        !           413:             args += 2;
        !           414:             break;
        !           415:         CASE_OP_32_64(not):
        !           416:         CASE_OP_32_64(neg):
        !           417:         CASE_OP_32_64(ext8s):
        !           418:         CASE_OP_32_64(ext8u):
        !           419:         CASE_OP_32_64(ext16s):
        !           420:         CASE_OP_32_64(ext16u):
        !           421:         case INDEX_op_ext32s_i64:
        !           422:         case INDEX_op_ext32u_i64:
        !           423:             if (temps[args[1]].state == TCG_TEMP_CONST) {
        !           424:                 gen_opc_buf[op_index] = op_to_movi(op);
        !           425:                 tmp = do_constant_folding(op, temps[args[1]].val, 0);
        !           426:                 tcg_opt_gen_movi(gen_args, args[0], tmp, nb_temps, nb_globals);
        !           427:                 gen_args += 2;
        !           428:                 args += 2;
        !           429:                 break;
        !           430:             } else {
        !           431:                 reset_temp(args[0], nb_temps, nb_globals);
        !           432:                 gen_args[0] = args[0];
        !           433:                 gen_args[1] = args[1];
        !           434:                 gen_args += 2;
        !           435:                 args += 2;
        !           436:                 break;
        !           437:             }
        !           438:         CASE_OP_32_64(add):
        !           439:         CASE_OP_32_64(sub):
        !           440:         CASE_OP_32_64(mul):
        !           441:         CASE_OP_32_64(or):
        !           442:         CASE_OP_32_64(and):
        !           443:         CASE_OP_32_64(xor):
        !           444:         CASE_OP_32_64(shl):
        !           445:         CASE_OP_32_64(shr):
        !           446:         CASE_OP_32_64(sar):
        !           447:         CASE_OP_32_64(rotl):
        !           448:         CASE_OP_32_64(rotr):
        !           449:         CASE_OP_32_64(andc):
        !           450:         CASE_OP_32_64(orc):
        !           451:         CASE_OP_32_64(eqv):
        !           452:         CASE_OP_32_64(nand):
        !           453:         CASE_OP_32_64(nor):
        !           454:             if (temps[args[1]].state == TCG_TEMP_CONST
        !           455:                 && temps[args[2]].state == TCG_TEMP_CONST) {
        !           456:                 gen_opc_buf[op_index] = op_to_movi(op);
        !           457:                 tmp = do_constant_folding(op, temps[args[1]].val,
        !           458:                                           temps[args[2]].val);
        !           459:                 tcg_opt_gen_movi(gen_args, args[0], tmp, nb_temps, nb_globals);
        !           460:                 gen_args += 2;
        !           461:                 args += 3;
        !           462:                 break;
        !           463:             } else {
        !           464:                 reset_temp(args[0], nb_temps, nb_globals);
        !           465:                 gen_args[0] = args[0];
        !           466:                 gen_args[1] = args[1];
        !           467:                 gen_args[2] = args[2];
        !           468:                 gen_args += 3;
        !           469:                 args += 3;
        !           470:                 break;
        !           471:             }
        !           472:         case INDEX_op_call:
        !           473:             nb_call_args = (args[0] >> 16) + (args[0] & 0xffff);
        !           474:             if (!(args[nb_call_args + 1] & (TCG_CALL_CONST | TCG_CALL_PURE))) {
        !           475:                 for (i = 0; i < nb_globals; i++) {
        !           476:                     reset_temp(i, nb_temps, nb_globals);
        !           477:                 }
        !           478:             }
        !           479:             for (i = 0; i < (args[0] >> 16); i++) {
        !           480:                 reset_temp(args[i + 1], nb_temps, nb_globals);
        !           481:             }
        !           482:             i = nb_call_args + 3;
        !           483:             while (i) {
        !           484:                 *gen_args = *args;
        !           485:                 args++;
        !           486:                 gen_args++;
        !           487:                 i--;
        !           488:             }
        !           489:             break;
        !           490:         case INDEX_op_set_label:
        !           491:         case INDEX_op_jmp:
        !           492:         case INDEX_op_br:
        !           493:         CASE_OP_32_64(brcond):
        !           494:             memset(temps, 0, nb_temps * sizeof(struct tcg_temp_info));
        !           495:             for (i = 0; i < def->nb_args; i++) {
        !           496:                 *gen_args = *args;
        !           497:                 args++;
        !           498:                 gen_args++;
        !           499:             }
        !           500:             break;
        !           501:         default:
        !           502:             /* Default case: we do know nothing about operation so no
        !           503:                propagation is done.  We only trash output args.  */
        !           504:             for (i = 0; i < def->nb_oargs; i++) {
        !           505:                 reset_temp(args[i], nb_temps, nb_globals);
        !           506:             }
        !           507:             for (i = 0; i < def->nb_args; i++) {
        !           508:                 gen_args[i] = args[i];
        !           509:             }
        !           510:             args += def->nb_args;
        !           511:             gen_args += def->nb_args;
        !           512:             break;
        !           513:         }
        !           514:     }
        !           515: 
        !           516:     return gen_args;
        !           517: }
        !           518: 
        !           519: TCGArg *tcg_optimize(TCGContext *s, uint16_t *tcg_opc_ptr,
        !           520:         TCGArg *args, TCGOpDef *tcg_op_defs)
        !           521: {
        !           522:     TCGArg *res;
        !           523:     res = tcg_constant_folding(s, tcg_opc_ptr, args, tcg_op_defs);
        !           524:     return res;
        !           525: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.