Skip to content

Commit 3e9fc5d

Browse files
wuxinyangSendaoYan
authored andcommitted
8376164: Optimize AES/ECB implementation using full-message intrinsic stub and parallel RoundKey addition
Reviewed-by: sviswanathan, semery
1 parent 6808ba2 commit 3e9fc5d

2 files changed

Lines changed: 212 additions & 3 deletions

File tree

src/hotspot/cpu/x86/stubGenerator_x86_64.hpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -330,6 +330,19 @@ class StubGenerator: public StubCodeGenerator {
330330

331331
void aesecb_decrypt(Register source_addr, Register dest_addr, Register key, Register len);
332332

333+
// Shared implementation for ECB/AES Encrypt and Decrypt, which does 4 blocks
334+
// in a loop at a time to hide instruction latency. Set is_encrypt=true for
335+
// encryption, false for decryption.
336+
address generate_electronicCodeBook_AESCrypt_Parallel(bool is_encrypt);
337+
338+
// A version of ECB/AES Encrypt which does 4 blocks in a loop at a time
339+
// to hide instruction latency
340+
address generate_electronicCodeBook_encryptAESCrypt_Parallel();
341+
342+
// A version of ECB/AES Decrypt which does 4 blocks in a loop at a time
343+
// to hide instruction latency
344+
address generate_electronicCodeBook_decryptAESCrypt_Parallel();
345+
333346
// Vector AES Galois Counter Mode implementation
334347
address generate_galoisCounterMode_AESCrypt();
335348
void aesgcm_encrypt(Register in, Register len, Register ct, Register out, Register key,

src/hotspot/cpu/x86/stubGenerator_x86_64_aes.cpp

Lines changed: 198 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2025, Intel Corporation. All rights reserved.
2+
* Copyright (c) 2019, 2026, Intel Corporation. All rights reserved.
33
*
44
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
55
*
@@ -218,6 +218,8 @@ void StubGenerator::generate_aes_stubs() {
218218
StubRoutines::_galoisCounterMode_AESCrypt = generate_galoisCounterMode_AESCrypt();
219219
} else {
220220
StubRoutines::_cipherBlockChaining_decryptAESCrypt = generate_cipherBlockChaining_decryptAESCrypt_Parallel();
221+
StubRoutines::_electronicCodeBook_encryptAESCrypt = generate_electronicCodeBook_encryptAESCrypt_Parallel();
222+
StubRoutines::_electronicCodeBook_decryptAESCrypt = generate_electronicCodeBook_decryptAESCrypt_Parallel();
221223
if (VM_Version::supports_avx2()) {
222224
StubRoutines::_galoisCounterMode_AESCrypt = generate_avx2_galoisCounterMode_AESCrypt();
223225
}
@@ -1399,6 +1401,200 @@ address StubGenerator::generate_cipherBlockChaining_encryptAESCrypt() {
13991401
return start;
14001402
}
14011403

1404+
// This is a version of ECB/AES Encrypt/Decrypt which does 4 blocks in a loop
1405+
// at a time to hide instruction latency.
1406+
//
1407+
// For encryption (is_encrypt=true):
1408+
// pxor key[0], aesenc key[1..rounds-1], aesenclast key[rounds]
1409+
// For decryption (is_encrypt=false):
1410+
// pxor key[1], aesdec key[2..rounds], aesdeclast key[0]
1411+
//
1412+
// Arguments:
1413+
//
1414+
// Inputs:
1415+
// c_rarg0 - source byte array address
1416+
// c_rarg1 - destination byte array address
1417+
// c_rarg2 - session key (Ke/Kd) in little endian int array
1418+
// c_rarg3 - input length (must be multiple of blocksize 16)
1419+
//
1420+
// Output:
1421+
// rax - input length
1422+
//
1423+
address StubGenerator::generate_electronicCodeBook_AESCrypt_Parallel(bool is_encrypt) {
1424+
assert(UseAES, "need AES instructions and misaligned SSE support");
1425+
__ align(CodeEntryAlignment);
1426+
StubId stub_id = is_encrypt ? StubId::stubgen_electronicCodeBook_encryptAESCrypt_id
1427+
: StubId::stubgen_electronicCodeBook_decryptAESCrypt_id;
1428+
StubCodeMark mark(this, stub_id);
1429+
address start = __ pc();
1430+
1431+
const Register from = c_rarg0; // source array address
1432+
const Register to = c_rarg1; // destination array address
1433+
const Register key = c_rarg2; // key array address
1434+
const Register len_reg = c_rarg3; // src len (must be multiple of blocksize 16)
1435+
const Register pos = rax;
1436+
const Register keylen = r11;
1437+
1438+
const XMMRegister xmm_result0 = xmm0;
1439+
const XMMRegister xmm_result1 = xmm1;
1440+
const XMMRegister xmm_result2 = xmm2;
1441+
const XMMRegister xmm_result3 = xmm3;
1442+
const XMMRegister xmm_key_shuf_mask = xmm4;
1443+
const XMMRegister xmm_key_tmp = xmm5;
1444+
// keys 0-9 pre-loaded into xmm6-xmm15
1445+
const int XMM_REG_NUM_KEY_FIRST = 6;
1446+
const int XMM_REG_NUM_KEY_LAST = 15;
1447+
const XMMRegister xmm_key_first = as_XMMRegister(XMM_REG_NUM_KEY_FIRST);
1448+
1449+
// for key_128, key_192, key_256
1450+
const int ROUNDS[3] = {10, 12, 14};
1451+
1452+
Label L_exit;
1453+
Label L_loop4[3], L_single[3], L_done[3];
1454+
1455+
#ifdef DoFour
1456+
#undef DoFour
1457+
#endif
1458+
#ifdef DoOne
1459+
#undef DoOne
1460+
#endif
1461+
1462+
#define DoFour(opc, reg) \
1463+
__ opc(xmm_result0, reg); \
1464+
__ opc(xmm_result1, reg); \
1465+
__ opc(xmm_result2, reg); \
1466+
__ opc(xmm_result3, reg);
1467+
1468+
#define DoOne(opc, reg) \
1469+
__ opc(xmm_result0, reg);
1470+
1471+
__ enter(); // required for proper stackwalking of RuntimeStub frame
1472+
__ push(len_reg); // save original length for return value
1473+
1474+
__ movl(keylen, Address(key, arrayOopDesc::length_offset_in_bytes() - arrayOopDesc::base_offset_in_bytes(T_INT)));
1475+
1476+
__ movdqu(xmm_key_shuf_mask, ExternalAddress(key_shuffle_mask_addr()), r10 /*rscratch*/);
1477+
// load up xmm regs 6 thru 15 with keys 0x00 - 0x90
1478+
for (int rnum = XMM_REG_NUM_KEY_FIRST, offset = 0x00; rnum <= XMM_REG_NUM_KEY_LAST; rnum++, offset += 0x10) {
1479+
load_key(as_XMMRegister(rnum), key, offset, xmm_key_shuf_mask);
1480+
}
1481+
__ xorptr(pos, pos);
1482+
1483+
// key length could be only {11, 13, 15} * 4 = {44, 52, 60}
1484+
__ cmpl(keylen, 52);
1485+
__ jcc(Assembler::equal, L_loop4[1]);
1486+
__ cmpl(keylen, 60);
1487+
__ jcc(Assembler::equal, L_loop4[2]);
1488+
1489+
// k == 0: generate code for key_128
1490+
// k == 1: generate code for key_192
1491+
// k == 2: generate code for key_256
1492+
for (int k = 0; k < 3; ++k) {
1493+
__ align(OptoLoopAlignment);
1494+
__ BIND(L_loop4[k]);
1495+
__ cmpptr(len_reg, 4 * AESBlockSize);
1496+
__ jcc(Assembler::less, L_single[k]);
1497+
1498+
__ movdqu(xmm_result0, Address(from, pos, Address::times_1, 0 * AESBlockSize));
1499+
__ movdqu(xmm_result1, Address(from, pos, Address::times_1, 1 * AESBlockSize));
1500+
__ movdqu(xmm_result2, Address(from, pos, Address::times_1, 2 * AESBlockSize));
1501+
__ movdqu(xmm_result3, Address(from, pos, Address::times_1, 3 * AESBlockSize));
1502+
1503+
if (is_encrypt) {
1504+
DoFour(pxor, xmm_key_first);
1505+
for (int rnum = 1; rnum < 10; rnum++) {
1506+
DoFour(aesenc, as_XMMRegister(rnum + XMM_REG_NUM_KEY_FIRST));
1507+
}
1508+
for (int i = 10; i < ROUNDS[k]; i++) {
1509+
load_key(xmm_key_tmp, key, i * 0x10, xmm_key_shuf_mask);
1510+
DoFour(aesenc, xmm_key_tmp);
1511+
}
1512+
load_key(xmm_key_tmp, key, ROUNDS[k] * 0x10, xmm_key_shuf_mask);
1513+
DoFour(aesenclast, xmm_key_tmp);
1514+
} else {
1515+
DoFour(pxor, as_XMMRegister(1 + XMM_REG_NUM_KEY_FIRST));
1516+
for (int rnum = 2; rnum < 10; rnum++) {
1517+
DoFour(aesdec, as_XMMRegister(rnum + XMM_REG_NUM_KEY_FIRST));
1518+
}
1519+
for (int i = 10; i <= ROUNDS[k]; i++) {
1520+
load_key(xmm_key_tmp, key, i * 0x10, xmm_key_shuf_mask);
1521+
DoFour(aesdec, xmm_key_tmp);
1522+
}
1523+
DoFour(aesdeclast, xmm_key_first);
1524+
}
1525+
1526+
__ movdqu(Address(to, pos, Address::times_1, 0 * AESBlockSize), xmm_result0);
1527+
__ movdqu(Address(to, pos, Address::times_1, 1 * AESBlockSize), xmm_result1);
1528+
__ movdqu(Address(to, pos, Address::times_1, 2 * AESBlockSize), xmm_result2);
1529+
__ movdqu(Address(to, pos, Address::times_1, 3 * AESBlockSize), xmm_result3);
1530+
1531+
__ addptr(pos, 4 * AESBlockSize);
1532+
__ subptr(len_reg, 4 * AESBlockSize);
1533+
__ jmp(L_loop4[k]);
1534+
1535+
__ align(OptoLoopAlignment);
1536+
__ BIND(L_single[k]);
1537+
__ cmpptr(len_reg, AESBlockSize);
1538+
__ jcc(Assembler::less, L_done[k]);
1539+
1540+
__ movdqu(xmm_result0, Address(from, pos, Address::times_1, 0));
1541+
1542+
if (is_encrypt) {
1543+
DoOne(pxor, xmm_key_first);
1544+
for (int rnum = 1; rnum < 10; rnum++) {
1545+
DoOne(aesenc, as_XMMRegister(rnum + XMM_REG_NUM_KEY_FIRST));
1546+
}
1547+
for (int i = 10; i < ROUNDS[k]; i++) {
1548+
load_key(xmm_key_tmp, key, i * 0x10, xmm_key_shuf_mask);
1549+
DoOne(aesenc, xmm_key_tmp);
1550+
}
1551+
load_key(xmm_key_tmp, key, ROUNDS[k] * 0x10, xmm_key_shuf_mask);
1552+
DoOne(aesenclast, xmm_key_tmp);
1553+
} else {
1554+
DoOne(pxor, as_XMMRegister(1 + XMM_REG_NUM_KEY_FIRST));
1555+
for (int rnum = 2; rnum < 10; rnum++) {
1556+
DoOne(aesdec, as_XMMRegister(rnum + XMM_REG_NUM_KEY_FIRST));
1557+
}
1558+
for (int i = 10; i <= ROUNDS[k]; i++) {
1559+
load_key(xmm_key_tmp, key, i * 0x10, xmm_key_shuf_mask);
1560+
DoOne(aesdec, xmm_key_tmp);
1561+
}
1562+
DoOne(aesdeclast, xmm_key_first);
1563+
}
1564+
1565+
__ movdqu(Address(to, pos, Address::times_1, 0), xmm_result0);
1566+
__ addptr(pos, AESBlockSize);
1567+
__ subptr(len_reg, AESBlockSize);
1568+
__ jmp(L_single[k]);
1569+
1570+
__ BIND(L_done[k]);
1571+
if (k < 2) __ jmp(L_exit);
1572+
} //for key_128/192/256
1573+
1574+
__ BIND(L_exit);
1575+
// Clear all XMM registers holding sensitive key material before returning
1576+
__ pxor(xmm_key_tmp, xmm_key_tmp);
1577+
for (int rnum = XMM_REG_NUM_KEY_FIRST; rnum <= XMM_REG_NUM_KEY_LAST; rnum++) {
1578+
__ pxor(as_XMMRegister(rnum), as_XMMRegister(rnum));
1579+
}
1580+
__ pop(rax);
1581+
__ leave(); // required for proper stackwalking of RuntimeStub frame
1582+
__ ret(0);
1583+
1584+
return start;
1585+
1586+
#undef DoFour
1587+
#undef DoOne
1588+
}
1589+
1590+
address StubGenerator::generate_electronicCodeBook_encryptAESCrypt_Parallel() {
1591+
return generate_electronicCodeBook_AESCrypt_Parallel(true);
1592+
}
1593+
1594+
address StubGenerator::generate_electronicCodeBook_decryptAESCrypt_Parallel() {
1595+
return generate_electronicCodeBook_AESCrypt_Parallel(false);
1596+
}
1597+
14021598
// This is a version of CBC/AES Decrypt which does 4 blocks in a loop at a time
14031599
// to hide instruction latency
14041600
//
@@ -1493,7 +1689,7 @@ address StubGenerator::generate_cipherBlockChaining_decryptAESCrypt_Parallel() {
14931689
__ opc(xmm_result0, src_reg); \
14941690
__ opc(xmm_result1, src_reg); \
14951691
__ opc(xmm_result2, src_reg); \
1496-
__ opc(xmm_result3, src_reg); \
1692+
__ opc(xmm_result3, src_reg);
14971693

14981694
for (int k = 0; k < 3; ++k) {
14991695
__ BIND(L_multiBlock_loopTopHead[k]);

0 commit comments

Comments
 (0)