The usage of fence.i in openjdk
Vladimir Kempik
vladimir.kempik at gmail.com
Fri Aug 5 22:06:32 UTC 2022
More on this subject
I can see the use of ifence() in the code is identical to the use of isb() in aarch64.
Checking the documentation for fence.i and isb, I don’t see them to be 1:1 identical
fence.i ( https://five-embeddev.com/riscv-isa-manual/latest/zifencei.html <https://five-embeddev.com/riscv-isa-manual/latest/zifencei.html> ):
FENCE.I instruction provides explicit synchronization between writes to instruction memory and instruction fetches on the same hart.
ISB ( https://developer.arm.com/documentation/den0024/a/Memory-Ordering/Barriers/ISB-in-more-detail ):
An ISB flushes the pipeline, and re-fetches the instructions from the cache or memory and ensures that the effects of any completed context-changing operation before the ISB are visible to any instruction after the ISB. It also ensures that any context-changing operations after the ISB instruction only take effect after the ISB has been executed and are not seen by instructions before the ISB.
And some info from the web:
To me it sound like isb ( in aarch64) does the job a bit different than fence.i ( in rv64)
So, I think here:
__ la_patchable(t0, RuntimeAddress(CAST_FROM_FN_PTR(address, SharedRuntime::fixup_callers_callsite)), offset);
__ jalr(x1, t0, offset);
// Explicit fence.i required because fixup_callers_callsite may change the code
// stream.
__ safepoint_ifence();
__ pop_CPU_state();
// restore sp
__ leave();
__ bind(L);
we still have a small chance to start executing invalid ( old) code from l1i if right after safepoint_ifence() our thread would be moved to another hart. Otherwise if fixup_callers_callsite would call icache_flush() somewhere inside, then safepoint_ifence wouldn’t be needed here
Regards, Vladimir
> 30 июля 2022 г., в 13:29, Vladimir Kempik <vladimir.kempik at gmail.com> написал(а):
>
> Hello
> Thanks for explanation.
> that sounds like the fence.i in userspace code is not needed at all
> Regards, Vladimir
>> 30 июля 2022 г., в 05:41, wangyadong (E) <yadonn.wang at huawei.com> написал(а):
>>
>>> Lets say you have a thread A running on hart 1.
>>> You've changed some code in region 0x11223300 and need fence.i before executing that code.
>>> you execute fence.i in your thread A running on hart 1.
>>> right after that your thread ( for some reason) got rescheduled ( by kernel) to hart 2.
>>> if hart 2 had something in l1i corresponding to region 0x11223300, then you gonna have a problem: l1i on hart 2 has old code, it wasn’t refreshed, because fence.i was executed on hart 1 ( and never on hart 2). And you thread gonna execute old code, or mix of old and new code.
>>
>> @vladimir Thanks for your explanation. I understand your concern now. We know the fence.i's scope, so the write hart does not rely solely on the fence.i in RISC-V port, but calls the icache_flush syscall in ICache::invalidate_range() every time after modifying the code.
>>
>> For example:
>> Hart 1
>> void MacroAssembler::emit_static_call_stub() {
>> // CompiledDirectStaticCall::set_to_interpreted knows the
>> // exact layout of this stub.
>>
>> ifence();
>> mov_metadata(xmethod, (Metadata*)NULL); <- patchable code here
>>
>> // Jump to the entry point of the i2c stub.
>> int32_t offset = 0;
>> movptr_with_offset(t0, 0, offset);
>> jalr(x0, t0, offset);
>> }
>>
>> Hart 2 (write hart)
>> void NativeMovConstReg::set_data(intptr_t x) {
>> // ...
>> // Store x into the instruction stream.
>> MacroAssembler::pd_patch_instruction_size(instruction_address(), (address)x); <- write code
>> ICache::invalidate_range(instruction_address(), movptr_instruction_size); <- syscall here
>> // ...
>> }
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/riscv-port-dev/attachments/20220806/87157cb9/attachment.htm>
More information about the riscv-port-dev
mailing list