Path: blob/master/src/hotspot/cpu/ppc/c2_MacroAssembler_ppc.cpp
64440 views
/*1* Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324#include "precompiled.hpp"25#include "asm/assembler.hpp"26#include "asm/assembler.inline.hpp"27#include "opto/c2_MacroAssembler.hpp"28#include "opto/intrinsicnode.hpp"29#include "runtime/vm_version.hpp"3031#ifdef PRODUCT32#define BLOCK_COMMENT(str) // nothing33#else34#define BLOCK_COMMENT(str) block_comment(str)35#endif36#define BIND(label) bind(label); BLOCK_COMMENT(#label ":")3738// Intrinsics for CompactStrings3940// Compress char[] to byte[] by compressing 16 bytes at once.41void C2_MacroAssembler::string_compress_16(Register src, Register dst, Register cnt,42Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5,43Label& Lfailure, bool ascii) {4445const Register tmp0 = R0;46const int byte_mask = ascii ? 0x7F : 0xFF;47assert_different_registers(src, dst, cnt, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5);48Label Lloop, Lslow;4950// Check if cnt >= 8 (= 16 bytes)51lis(tmp1, byte_mask); // tmp1 = 0x00FF00FF00FF00FF (non ascii case)52srwi_(tmp2, cnt, 3);53beq(CCR0, Lslow);54ori(tmp1, tmp1, byte_mask);55rldimi(tmp1, tmp1, 32, 0);56mtctr(tmp2);5758// 2x unrolled loop59bind(Lloop);60ld(tmp2, 0, src); // _0_1_2_3 (Big Endian)61ld(tmp4, 8, src); // _4_5_6_76263orr(tmp0, tmp2, tmp4);64rldicl(tmp3, tmp2, 6*8, 64-24); // _____1_265rldimi(tmp2, tmp2, 2*8, 2*8); // _0_2_3_366rldicl(tmp5, tmp4, 6*8, 64-24); // _____5_667rldimi(tmp4, tmp4, 2*8, 2*8); // _4_6_7_76869andc_(tmp0, tmp0, tmp1);70bne(CCR0, Lfailure); // Not latin1/ascii.71addi(src, src, 16);7273rlwimi(tmp3, tmp2, 0*8, 24, 31);// _____1_374srdi(tmp2, tmp2, 3*8); // ____0_2_75rlwimi(tmp5, tmp4, 0*8, 24, 31);// _____5_776srdi(tmp4, tmp4, 3*8); // ____4_6_7778orr(tmp2, tmp2, tmp3); // ____012379orr(tmp4, tmp4, tmp5); // ____45678081stw(tmp2, 0, dst);82stw(tmp4, 4, dst);83addi(dst, dst, 8);84bdnz(Lloop);8586bind(Lslow); // Fallback to slow version87}8889// Compress char[] to byte[]. cnt must be positive int.90void C2_MacroAssembler::string_compress(Register src, Register dst, Register cnt, Register tmp,91Label& Lfailure, bool ascii) {92const int byte_mask = ascii ? 0x7F : 0xFF;93Label Lloop;94mtctr(cnt);9596bind(Lloop);97lhz(tmp, 0, src);98cmplwi(CCR0, tmp, byte_mask);99bgt(CCR0, Lfailure); // Not latin1/ascii.100addi(src, src, 2);101stb(tmp, 0, dst);102addi(dst, dst, 1);103bdnz(Lloop);104}105106void C2_MacroAssembler::encode_iso_array(Register src, Register dst, Register len,107Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5,108Register result, bool ascii) {109Label Lslow, Lfailure1, Lfailure2, Ldone;110111string_compress_16(src, dst, len, tmp1, tmp2, tmp3, tmp4, tmp5, Lfailure1, ascii);112rldicl_(result, len, 0, 64-3); // Remaining characters.113beq(CCR0, Ldone);114bind(Lslow);115string_compress(src, dst, result, tmp2, Lfailure2, ascii);116li(result, 0);117b(Ldone);118119bind(Lfailure1);120mr(result, len);121mfctr(tmp1);122rldimi_(result, tmp1, 3, 0); // Remaining characters.123beq(CCR0, Ldone);124b(Lslow);125126bind(Lfailure2);127mfctr(result); // Remaining characters.128129bind(Ldone);130subf(result, result, len);131}132133// Inflate byte[] to char[] by inflating 16 bytes at once.134void C2_MacroAssembler::string_inflate_16(Register src, Register dst, Register cnt,135Register tmp1, Register tmp2, Register tmp3, Register tmp4, Register tmp5) {136const Register tmp0 = R0;137assert_different_registers(src, dst, cnt, tmp0, tmp1, tmp2, tmp3, tmp4, tmp5);138Label Lloop, Lslow;139140// Check if cnt >= 8141srwi_(tmp2, cnt, 3);142beq(CCR0, Lslow);143lis(tmp1, 0xFF); // tmp1 = 0x00FF00FF144ori(tmp1, tmp1, 0xFF);145mtctr(tmp2);146147// 2x unrolled loop148bind(Lloop);149lwz(tmp2, 0, src); // ____0123 (Big Endian)150lwz(tmp4, 4, src); // ____4567151addi(src, src, 8);152153rldicl(tmp3, tmp2, 7*8, 64-8); // _______2154rlwimi(tmp2, tmp2, 3*8, 16, 23);// ____0113155rldicl(tmp5, tmp4, 7*8, 64-8); // _______6156rlwimi(tmp4, tmp4, 3*8, 16, 23);// ____4557157158andc(tmp0, tmp2, tmp1); // ____0_1_159rlwimi(tmp2, tmp3, 2*8, 0, 23); // _____2_3160andc(tmp3, tmp4, tmp1); // ____4_5_161rlwimi(tmp4, tmp5, 2*8, 0, 23); // _____6_7162163rldimi(tmp2, tmp0, 3*8, 0*8); // _0_1_2_3164rldimi(tmp4, tmp3, 3*8, 0*8); // _4_5_6_7165166std(tmp2, 0, dst);167std(tmp4, 8, dst);168addi(dst, dst, 16);169bdnz(Lloop);170171bind(Lslow); // Fallback to slow version172}173174// Inflate byte[] to char[]. cnt must be positive int.175void C2_MacroAssembler::string_inflate(Register src, Register dst, Register cnt, Register tmp) {176Label Lloop;177mtctr(cnt);178179bind(Lloop);180lbz(tmp, 0, src);181addi(src, src, 1);182sth(tmp, 0, dst);183addi(dst, dst, 2);184bdnz(Lloop);185}186187void C2_MacroAssembler::string_compare(Register str1, Register str2,188Register cnt1, Register cnt2,189Register tmp1, Register result, int ae) {190const Register tmp0 = R0,191diff = tmp1;192193assert_different_registers(str1, str2, cnt1, cnt2, tmp0, tmp1, result);194Label Ldone, Lslow, Lloop, Lreturn_diff;195196// Note: Making use of the fact that compareTo(a, b) == -compareTo(b, a)197// we interchange str1 and str2 in the UL case and negate the result.198// Like this, str1 is always latin1 encoded, except for the UU case.199// In addition, we need 0 (or sign which is 0) extend.200201if (ae == StrIntrinsicNode::UU) {202srwi(cnt1, cnt1, 1);203} else {204clrldi(cnt1, cnt1, 32);205}206207if (ae != StrIntrinsicNode::LL) {208srwi(cnt2, cnt2, 1);209} else {210clrldi(cnt2, cnt2, 32);211}212213// See if the lengths are different, and calculate min in cnt1.214// Save diff in case we need it for a tie-breaker.215subf_(diff, cnt2, cnt1); // diff = cnt1 - cnt2216// if (diff > 0) { cnt1 = cnt2; }217if (VM_Version::has_isel()) {218isel(cnt1, CCR0, Assembler::greater, /*invert*/ false, cnt2);219} else {220Label Lskip;221blt(CCR0, Lskip);222mr(cnt1, cnt2);223bind(Lskip);224}225226// Rename registers227Register chr1 = result;228Register chr2 = tmp0;229230// Compare multiple characters in fast loop (only implemented for same encoding).231int stride1 = 8, stride2 = 8;232if (ae == StrIntrinsicNode::LL || ae == StrIntrinsicNode::UU) {233int log2_chars_per_iter = (ae == StrIntrinsicNode::LL) ? 3 : 2;234Label Lfastloop, Lskipfast;235236srwi_(tmp0, cnt1, log2_chars_per_iter);237beq(CCR0, Lskipfast);238rldicl(cnt2, cnt1, 0, 64 - log2_chars_per_iter); // Remaining characters.239li(cnt1, 1 << log2_chars_per_iter); // Initialize for failure case: Rescan characters from current iteration.240mtctr(tmp0);241242bind(Lfastloop);243ld(chr1, 0, str1);244ld(chr2, 0, str2);245cmpd(CCR0, chr1, chr2);246bne(CCR0, Lslow);247addi(str1, str1, stride1);248addi(str2, str2, stride2);249bdnz(Lfastloop);250mr(cnt1, cnt2); // Remaining characters.251bind(Lskipfast);252}253254// Loop which searches the first difference character by character.255cmpwi(CCR0, cnt1, 0);256beq(CCR0, Lreturn_diff);257bind(Lslow);258mtctr(cnt1);259260switch (ae) {261case StrIntrinsicNode::LL: stride1 = 1; stride2 = 1; break;262case StrIntrinsicNode::UL: // fallthru (see comment above)263case StrIntrinsicNode::LU: stride1 = 1; stride2 = 2; break;264case StrIntrinsicNode::UU: stride1 = 2; stride2 = 2; break;265default: ShouldNotReachHere(); break;266}267268bind(Lloop);269if (stride1 == 1) { lbz(chr1, 0, str1); } else { lhz(chr1, 0, str1); }270if (stride2 == 1) { lbz(chr2, 0, str2); } else { lhz(chr2, 0, str2); }271subf_(result, chr2, chr1); // result = chr1 - chr2272bne(CCR0, Ldone);273addi(str1, str1, stride1);274addi(str2, str2, stride2);275bdnz(Lloop);276277// If strings are equal up to min length, return the length difference.278bind(Lreturn_diff);279mr(result, diff);280281// Otherwise, return the difference between the first mismatched chars.282bind(Ldone);283if (ae == StrIntrinsicNode::UL) {284neg(result, result); // Negate result (see note above).285}286}287288void C2_MacroAssembler::array_equals(bool is_array_equ, Register ary1, Register ary2,289Register limit, Register tmp1, Register result, bool is_byte) {290const Register tmp0 = R0;291assert_different_registers(ary1, ary2, limit, tmp0, tmp1, result);292Label Ldone, Lskiploop, Lloop, Lfastloop, Lskipfast;293bool limit_needs_shift = false;294295if (is_array_equ) {296const int length_offset = arrayOopDesc::length_offset_in_bytes();297const int base_offset = arrayOopDesc::base_offset_in_bytes(is_byte ? T_BYTE : T_CHAR);298299// Return true if the same array.300cmpd(CCR0, ary1, ary2);301beq(CCR0, Lskiploop);302303// Return false if one of them is NULL.304cmpdi(CCR0, ary1, 0);305cmpdi(CCR1, ary2, 0);306li(result, 0);307cror(CCR0, Assembler::equal, CCR1, Assembler::equal);308beq(CCR0, Ldone);309310// Load the lengths of arrays.311lwz(limit, length_offset, ary1);312lwz(tmp0, length_offset, ary2);313314// Return false if the two arrays are not equal length.315cmpw(CCR0, limit, tmp0);316bne(CCR0, Ldone);317318// Load array addresses.319addi(ary1, ary1, base_offset);320addi(ary2, ary2, base_offset);321} else {322limit_needs_shift = !is_byte;323li(result, 0); // Assume not equal.324}325326// Rename registers327Register chr1 = tmp0;328Register chr2 = tmp1;329330// Compare 8 bytes per iteration in fast loop.331const int log2_chars_per_iter = is_byte ? 3 : 2;332333srwi_(tmp0, limit, log2_chars_per_iter + (limit_needs_shift ? 1 : 0));334beq(CCR0, Lskipfast);335mtctr(tmp0);336337bind(Lfastloop);338ld(chr1, 0, ary1);339ld(chr2, 0, ary2);340addi(ary1, ary1, 8);341addi(ary2, ary2, 8);342cmpd(CCR0, chr1, chr2);343bne(CCR0, Ldone);344bdnz(Lfastloop);345346bind(Lskipfast);347rldicl_(limit, limit, limit_needs_shift ? 64 - 1 : 0, 64 - log2_chars_per_iter); // Remaining characters.348beq(CCR0, Lskiploop);349mtctr(limit);350351// Character by character.352bind(Lloop);353if (is_byte) {354lbz(chr1, 0, ary1);355lbz(chr2, 0, ary2);356addi(ary1, ary1, 1);357addi(ary2, ary2, 1);358} else {359lhz(chr1, 0, ary1);360lhz(chr2, 0, ary2);361addi(ary1, ary1, 2);362addi(ary2, ary2, 2);363}364cmpw(CCR0, chr1, chr2);365bne(CCR0, Ldone);366bdnz(Lloop);367368bind(Lskiploop);369li(result, 1); // All characters are equal.370bind(Ldone);371}372373void C2_MacroAssembler::string_indexof(Register result, Register haystack, Register haycnt,374Register needle, ciTypeArray* needle_values, Register needlecnt, int needlecntval,375Register tmp1, Register tmp2, Register tmp3, Register tmp4, int ae) {376377// Ensure 0<needlecnt<=haycnt in ideal graph as prerequisite!378Label L_TooShort, L_Found, L_NotFound, L_End;379Register last_addr = haycnt, // Kill haycnt at the beginning.380addr = tmp1,381n_start = tmp2,382ch1 = tmp3,383ch2 = R0;384385assert(ae != StrIntrinsicNode::LU, "Invalid encoding");386const int h_csize = (ae == StrIntrinsicNode::LL) ? 1 : 2;387const int n_csize = (ae == StrIntrinsicNode::UU) ? 2 : 1;388389// **************************************************************************************************390// Prepare for main loop: optimized for needle count >=2, bail out otherwise.391// **************************************************************************************************392393// Compute last haystack addr to use if no match gets found.394clrldi(haycnt, haycnt, 32); // Ensure positive int is valid as 64 bit value.395addi(addr, haystack, -h_csize); // Accesses use pre-increment.396if (needlecntval == 0) { // variable needlecnt397cmpwi(CCR6, needlecnt, 2);398clrldi(needlecnt, needlecnt, 32); // Ensure positive int is valid as 64 bit value.399blt(CCR6, L_TooShort); // Variable needlecnt: handle short needle separately.400}401402if (n_csize == 2) { lwz(n_start, 0, needle); } else { lhz(n_start, 0, needle); } // Load first 2 characters of needle.403404if (needlecntval == 0) { // variable needlecnt405subf(ch1, needlecnt, haycnt); // Last character index to compare is haycnt-needlecnt.406addi(needlecnt, needlecnt, -2); // Rest of needle.407} else { // constant needlecnt408guarantee(needlecntval != 1, "IndexOf with single-character needle must be handled separately");409assert((needlecntval & 0x7fff) == needlecntval, "wrong immediate");410addi(ch1, haycnt, -needlecntval); // Last character index to compare is haycnt-needlecnt.411if (needlecntval > 3) { li(needlecnt, needlecntval - 2); } // Rest of needle.412}413414if (h_csize == 2) { slwi(ch1, ch1, 1); } // Scale to number of bytes.415416if (ae ==StrIntrinsicNode::UL) {417srwi(tmp4, n_start, 1*8); // ___0418rlwimi(n_start, tmp4, 2*8, 0, 23); // _0_1419}420421add(last_addr, haystack, ch1); // Point to last address to compare (haystack+2*(haycnt-needlecnt)).422423// Main Loop (now we have at least 2 characters).424Label L_OuterLoop, L_InnerLoop, L_FinalCheck, L_Comp1, L_Comp2;425bind(L_OuterLoop); // Search for 1st 2 characters.426Register addr_diff = tmp4;427subf(addr_diff, addr, last_addr); // Difference between already checked address and last address to check.428addi(addr, addr, h_csize); // This is the new address we want to use for comparing.429srdi_(ch2, addr_diff, h_csize);430beq(CCR0, L_FinalCheck); // 2 characters left?431mtctr(ch2); // num of characters / 2432bind(L_InnerLoop); // Main work horse (2x unrolled search loop)433if (h_csize == 2) { // Load 2 characters of haystack (ignore alignment).434lwz(ch1, 0, addr);435lwz(ch2, 2, addr);436} else {437lhz(ch1, 0, addr);438lhz(ch2, 1, addr);439}440cmpw(CCR0, ch1, n_start); // Compare 2 characters (1 would be sufficient but try to reduce branches to CompLoop).441cmpw(CCR1, ch2, n_start);442beq(CCR0, L_Comp1); // Did we find the needle start?443beq(CCR1, L_Comp2);444addi(addr, addr, 2 * h_csize);445bdnz(L_InnerLoop);446bind(L_FinalCheck);447andi_(addr_diff, addr_diff, h_csize); // Remaining characters not covered by InnerLoop: (num of characters) & 1.448beq(CCR0, L_NotFound);449if (h_csize == 2) { lwz(ch1, 0, addr); } else { lhz(ch1, 0, addr); } // One position left at which we have to compare.450cmpw(CCR1, ch1, n_start);451beq(CCR1, L_Comp1);452bind(L_NotFound);453li(result, -1); // not found454b(L_End);455456// **************************************************************************************************457// Special Case: unfortunately, the variable needle case can be called with needlecnt<2458// **************************************************************************************************459if (needlecntval == 0) { // We have to handle these cases separately.460Label L_OneCharLoop;461bind(L_TooShort);462mtctr(haycnt);463if (n_csize == 2) { lhz(n_start, 0, needle); } else { lbz(n_start, 0, needle); } // First character of needle464bind(L_OneCharLoop);465if (h_csize == 2) { lhzu(ch1, 2, addr); } else { lbzu(ch1, 1, addr); }466cmpw(CCR1, ch1, n_start);467beq(CCR1, L_Found); // Did we find the one character needle?468bdnz(L_OneCharLoop);469li(result, -1); // Not found.470b(L_End);471}472473// **************************************************************************************************474// Regular Case Part II: compare rest of needle (first 2 characters have been compared already)475// **************************************************************************************************476477// Compare the rest478bind(L_Comp2);479addi(addr, addr, h_csize); // First comparison has failed, 2nd one hit.480bind(L_Comp1); // Addr points to possible needle start.481if (needlecntval != 2) { // Const needlecnt==2?482if (needlecntval != 3) {483if (needlecntval == 0) { beq(CCR6, L_Found); } // Variable needlecnt==2?484Register n_ind = tmp4,485h_ind = n_ind;486li(n_ind, 2 * n_csize); // First 2 characters are already compared, use index 2.487mtctr(needlecnt); // Decremented by 2, still > 0.488Label L_CompLoop;489bind(L_CompLoop);490if (ae ==StrIntrinsicNode::UL) {491h_ind = ch1;492sldi(h_ind, n_ind, 1);493}494if (n_csize == 2) { lhzx(ch2, needle, n_ind); } else { lbzx(ch2, needle, n_ind); }495if (h_csize == 2) { lhzx(ch1, addr, h_ind); } else { lbzx(ch1, addr, h_ind); }496cmpw(CCR1, ch1, ch2);497bne(CCR1, L_OuterLoop);498addi(n_ind, n_ind, n_csize);499bdnz(L_CompLoop);500} else { // No loop required if there's only one needle character left.501if (n_csize == 2) { lhz(ch2, 2 * 2, needle); } else { lbz(ch2, 2 * 1, needle); }502if (h_csize == 2) { lhz(ch1, 2 * 2, addr); } else { lbz(ch1, 2 * 1, addr); }503cmpw(CCR1, ch1, ch2);504bne(CCR1, L_OuterLoop);505}506}507// Return index ...508bind(L_Found);509subf(result, haystack, addr); // relative to haystack, ...510if (h_csize == 2) { srdi(result, result, 1); } // in characters.511bind(L_End);512} // string_indexof513514void C2_MacroAssembler::string_indexof_char(Register result, Register haystack, Register haycnt,515Register needle, jchar needleChar, Register tmp1, Register tmp2, bool is_byte) {516assert_different_registers(haystack, haycnt, needle, tmp1, tmp2);517518Label L_InnerLoop, L_FinalCheck, L_Found1, L_Found2, L_NotFound, L_End;519Register addr = tmp1,520ch1 = tmp2,521ch2 = R0;522523const int h_csize = is_byte ? 1 : 2;524525//4:526srwi_(tmp2, haycnt, 1); // Shift right by exact_log2(UNROLL_FACTOR).527mr(addr, haystack);528beq(CCR0, L_FinalCheck);529mtctr(tmp2); // Move to count register.530//8:531bind(L_InnerLoop); // Main work horse (2x unrolled search loop).532if (!is_byte) {533lhz(ch1, 0, addr);534lhz(ch2, 2, addr);535} else {536lbz(ch1, 0, addr);537lbz(ch2, 1, addr);538}539(needle != R0) ? cmpw(CCR0, ch1, needle) : cmplwi(CCR0, ch1, (unsigned int)needleChar);540(needle != R0) ? cmpw(CCR1, ch2, needle) : cmplwi(CCR1, ch2, (unsigned int)needleChar);541beq(CCR0, L_Found1); // Did we find the needle?542beq(CCR1, L_Found2);543addi(addr, addr, 2 * h_csize);544bdnz(L_InnerLoop);545//16:546bind(L_FinalCheck);547andi_(R0, haycnt, 1);548beq(CCR0, L_NotFound);549if (!is_byte) { lhz(ch1, 0, addr); } else { lbz(ch1, 0, addr); } // One position left at which we have to compare.550(needle != R0) ? cmpw(CCR1, ch1, needle) : cmplwi(CCR1, ch1, (unsigned int)needleChar);551beq(CCR1, L_Found1);552//21:553bind(L_NotFound);554li(result, -1); // Not found.555b(L_End);556557bind(L_Found2);558addi(addr, addr, h_csize);559//24:560bind(L_Found1); // Return index ...561subf(result, haystack, addr); // relative to haystack, ...562if (!is_byte) { srdi(result, result, 1); } // in characters.563bind(L_End);564} // string_indexof_char565566567void C2_MacroAssembler::has_negatives(Register src, Register cnt, Register result,568Register tmp1, Register tmp2) {569const Register tmp0 = R0;570assert_different_registers(src, result, cnt, tmp0, tmp1, tmp2);571Label Lfastloop, Lslow, Lloop, Lnoneg, Ldone;572573// Check if cnt >= 8 (= 16 bytes)574lis(tmp1, (int)(short)0x8080); // tmp1 = 0x8080808080808080575srwi_(tmp2, cnt, 4);576li(result, 1); // Assume there's a negative byte.577beq(CCR0, Lslow);578ori(tmp1, tmp1, 0x8080);579rldimi(tmp1, tmp1, 32, 0);580mtctr(tmp2);581582// 2x unrolled loop583bind(Lfastloop);584ld(tmp2, 0, src);585ld(tmp0, 8, src);586587orr(tmp0, tmp2, tmp0);588589and_(tmp0, tmp0, tmp1);590bne(CCR0, Ldone); // Found negative byte.591addi(src, src, 16);592593bdnz(Lfastloop);594595bind(Lslow); // Fallback to slow version596rldicl_(tmp0, cnt, 0, 64-4);597beq(CCR0, Lnoneg);598mtctr(tmp0);599bind(Lloop);600lbz(tmp0, 0, src);601addi(src, src, 1);602andi_(tmp0, tmp0, 0x80);603bne(CCR0, Ldone); // Found negative byte.604bdnz(Lloop);605bind(Lnoneg);606li(result, 0);607608bind(Ldone);609}610611612613